From d93f55e863c598de9126a0316a813f872b11e29f Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Wed, 20 Jun 2018 13:05:00 -0700 Subject: Remove some defers in hot paths in the filesystem code. PiperOrigin-RevId: 201401727 Change-Id: Ia5589882ba58a00efb522ab372e206b7e8e62aee --- pkg/sentry/fs/fsutil/inode_cached.go | 89 +++++++++++++++++++---------- pkg/sentry/fs/inode_overlay.go | 105 ++++++++++++++++++++++++----------- pkg/sentry/fs/ramfs/ramfs.go | 52 +++++++++-------- 3 files changed, 160 insertions(+), 86 deletions(-) (limited to 'pkg/sentry') diff --git a/pkg/sentry/fs/fsutil/inode_cached.go b/pkg/sentry/fs/fsutil/inode_cached.go index 484668735..7c0f96ac2 100644 --- a/pkg/sentry/fs/fsutil/inode_cached.go +++ b/pkg/sentry/fs/fsutil/inode_cached.go @@ -179,8 +179,9 @@ func (c *CachingInodeOperations) Release() { // UnstableAttr implements fs.InodeOperations.UnstableAttr. func (c *CachingInodeOperations) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) { c.attrMu.Lock() - defer c.attrMu.Unlock() - return c.attr, nil + attr := c.attr + c.attrMu.Unlock() + return attr, nil } // SetPermissions implements fs.InodeOperations.SetPermissions. @@ -463,15 +464,17 @@ func (c *CachingInodeOperations) Read(ctx context.Context, file *fs.File, dst us // // If Write partially fills src, a non-nil error is returned. func (c *CachingInodeOperations) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) { + // Hot path. Avoid defers. if src.NumBytes() == 0 { return 0, nil } c.attrMu.Lock() - defer c.attrMu.Unlock() // Compare Linux's mm/filemap.c:__generic_file_write_iter() => file_update_time(). c.touchModificationTimeLocked(ctx) - return src.CopyInTo(ctx, &inodeReadWriter{ctx, c, offset}) + n, err := src.CopyInTo(ctx, &inodeReadWriter{ctx, c, offset}) + c.attrMu.Unlock() + return n, err } type inodeReadWriter struct { @@ -482,15 +485,17 @@ type inodeReadWriter struct { // ReadToBlocks implements safemem.Reader.ReadToBlocks. func (rw *inodeReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { + // Hot path. Avoid defers. rw.c.dataMu.RLock() - defer rw.c.dataMu.RUnlock() // Compute the range to read. if rw.offset >= rw.c.attr.Size { + rw.c.dataMu.RUnlock() return 0, io.EOF } end := fs.ReadEndOffset(rw.offset, int64(dsts.NumBytes()), rw.c.attr.Size) if end == rw.offset { // dsts.NumBytes() == 0? + rw.c.dataMu.RUnlock() return 0, nil } @@ -504,6 +509,7 @@ func (rw *inodeReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { // Get internal mappings from the cache. ims, err := mem.MapInternal(seg.FileRangeOf(seg.Range().Intersect(mr)), usermem.Read) if err != nil { + rw.c.dataMu.RUnlock() return done, err } @@ -513,6 +519,7 @@ func (rw *inodeReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { rw.offset += int64(n) dsts = dsts.DropFirst64(n) if err != nil { + rw.c.dataMu.RUnlock() return done, err } @@ -529,6 +536,7 @@ func (rw *inodeReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { dsts = dsts.DropFirst64(n) // Partial reads are fine. But we must stop reading. if n != dst.NumBytes() || err != nil { + rw.c.dataMu.RUnlock() return done, err } @@ -539,38 +547,44 @@ func (rw *inodeReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { break } } + rw.c.dataMu.RUnlock() return done, nil } +// maybeGrowFile grows the file's size if data has been written past the old +// size. +// +// Preconditions: rw.c.attrMu and rw.c.dataMu bust be locked. +func (rw *inodeReadWriter) maybeGrowFile() { + // If the write ends beyond the file's previous size, it causes the + // file to grow. + if rw.offset > rw.c.attr.Size { + rw.c.attr.Size = rw.offset + rw.c.dirtyAttr.Size = true + } + if rw.offset > rw.c.attr.Usage { + // This is incorrect if CachingInodeOperations is caching a sparse + // file. (In Linux, keeping inode::i_blocks up to date is the + // filesystem's responsibility.) + rw.c.attr.Usage = rw.offset + rw.c.dirtyAttr.Usage = true + } +} + // WriteFromBlocks implements safemem.Writer.WriteFromBlocks. // // Preconditions: rw.c.attrMu must be locked. func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) { + // Hot path. Avoid defers. rw.c.dataMu.Lock() - defer rw.c.dataMu.Unlock() // Compute the range to write. end := fs.WriteEndOffset(rw.offset, int64(srcs.NumBytes())) if end == rw.offset { // srcs.NumBytes() == 0? + rw.c.dataMu.Unlock() return 0, nil } - defer func() { - // If the write ends beyond the file's previous size, it causes the - // file to grow. - if rw.offset > rw.c.attr.Size { - rw.c.attr.Size = rw.offset - rw.c.dirtyAttr.Size = true - } - if rw.offset > rw.c.attr.Usage { - // This is incorrect if CachingInodeOperations is caching a sparse - // file. (In Linux, keeping inode::i_blocks up to date is the - // filesystem's responsibility.) - rw.c.attr.Usage = rw.offset - rw.c.dirtyAttr.Usage = true - } - }() - mem := rw.c.platform.Memory() var done uint64 seg, gap := rw.c.cache.Find(uint64(rw.offset)) @@ -582,6 +596,8 @@ func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error segMR := seg.Range().Intersect(mr) ims, err := mem.MapInternal(seg.FileRangeOf(segMR), usermem.Write) if err != nil { + rw.maybeGrowFile() + rw.c.dataMu.Unlock() return done, err } @@ -592,6 +608,8 @@ func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error srcs = srcs.DropFirst64(n) rw.c.dirty.MarkDirty(segMR) if err != nil { + rw.maybeGrowFile() + rw.c.dataMu.Unlock() return done, err } @@ -608,6 +626,8 @@ func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error srcs = srcs.DropFirst64(n) // Partial writes are fine. But we must stop writing. if n != src.NumBytes() || err != nil { + rw.maybeGrowFile() + rw.c.dataMu.Unlock() return done, err } @@ -618,13 +638,15 @@ func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error break } } + rw.maybeGrowFile() + rw.c.dataMu.Unlock() return done, nil } // AddMapping implements memmap.Mappable.AddMapping. func (c *CachingInodeOperations) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { + // Hot path. Avoid defers. c.mapsMu.Lock() - defer c.mapsMu.Unlock() mapped := c.mappings.AddMapping(ms, ar, offset) // Do this unconditionally since whether we have c.backingFile.FD() >= 0 // can change across save/restore. @@ -636,13 +658,14 @@ func (c *CachingInodeOperations) AddMapping(ctx context.Context, ms memmap.Mappi usage.MemoryAccounting.Inc(r.Length(), usage.Mapped) } } + c.mapsMu.Unlock() return nil } // RemoveMapping implements memmap.Mappable.RemoveMapping. func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { + // Hot path. Avoid defers. c.mapsMu.Lock() - defer c.mapsMu.Unlock() unmapped := c.mappings.RemoveMapping(ms, ar, offset) for _, r := range unmapped { c.hostFileMapper.DecRefOn(r) @@ -653,6 +676,7 @@ func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.Ma usage.MemoryAccounting.Dec(r.Length(), usage.Mapped) } } + c.mapsMu.Unlock() return } @@ -661,7 +685,6 @@ func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.Ma // strategy. mem := c.platform.Memory() c.dataMu.Lock() - defer c.dataMu.Unlock() for _, r := range unmapped { if err := SyncDirty(ctx, r, &c.cache, &c.dirty, uint64(c.attr.Size), c.platform.Memory(), c.backingFile.WriteFromBlocksAt); err != nil { log.Warningf("Failed to writeback cached data %v: %v", r, err) @@ -669,6 +692,8 @@ func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.Ma c.cache.Drop(r, mem) c.dirty.KeepClean(r) } + c.dataMu.Unlock() + c.mapsMu.Unlock() } // CopyMapping implements memmap.Mappable.CopyMapping. @@ -678,6 +703,7 @@ func (c *CachingInodeOperations) CopyMapping(ctx context.Context, ms memmap.Mapp // Translate implements memmap.Mappable.Translate. func (c *CachingInodeOperations) Translate(ctx context.Context, required, optional memmap.MappableRange, at usermem.AccessType) ([]memmap.Translation, error) { + // Hot path. Avoid defer. if !c.forcePageCache && c.backingFile.FD() >= 0 { return []memmap.Translation{ { @@ -689,7 +715,6 @@ func (c *CachingInodeOperations) Translate(ctx context.Context, required, option } c.dataMu.Lock() - defer c.dataMu.Unlock() // Constrain translations to c.attr.Size (rounded up) to prevent // translation to pages that may be concurrently truncated. @@ -697,6 +722,7 @@ func (c *CachingInodeOperations) Translate(ctx context.Context, required, option var beyondEOF bool if required.End > pgend { if required.Start >= pgend { + c.dataMu.Unlock() return nil, &memmap.BusError{io.EOF} } beyondEOF = true @@ -726,6 +752,8 @@ func (c *CachingInodeOperations) Translate(ctx context.Context, required, option translatedEnd = segMR.End } + c.dataMu.Unlock() + // Don't return the error returned by c.cache.Fill if it occurred outside // of required. if translatedEnd < required.End && cerr != nil { @@ -797,9 +825,8 @@ func (c *CachingInodeOperations) MapInternal(fr platform.FileRange, at usermem.A // underlying host fd and CachingInodeOperations is used as the platform.File // during translation. func (c *CachingInodeOperations) IncRef(fr platform.FileRange) { + // Hot path. Avoid defers. c.dataMu.Lock() - defer c.dataMu.Unlock() - seg, gap := c.refs.Find(fr.Start) for { switch { @@ -815,6 +842,7 @@ func (c *CachingInodeOperations) IncRef(fr platform.FileRange) { seg, gap = c.refs.InsertWithoutMerging(gap, newRange, 1).NextNonEmpty() default: c.refs.MergeAdjacent(fr) + c.dataMu.Unlock() return } } @@ -824,9 +852,8 @@ func (c *CachingInodeOperations) IncRef(fr platform.FileRange) { // underlying host fd and CachingInodeOperations is used as the platform.File // during translation. func (c *CachingInodeOperations) DecRef(fr platform.FileRange) { + // Hot path. Avoid defers. c.dataMu.Lock() - defer c.dataMu.Unlock() - seg := c.refs.FindSegment(fr.Start) for seg.Ok() && seg.Start() < fr.End { @@ -842,4 +869,6 @@ func (c *CachingInodeOperations) DecRef(fr platform.FileRange) { } } c.refs.MergeAdjacent(fr) + c.dataMu.Unlock() + } diff --git a/pkg/sentry/fs/inode_overlay.go b/pkg/sentry/fs/inode_overlay.go index 343150bb8..53fbd1481 100644 --- a/pkg/sentry/fs/inode_overlay.go +++ b/pkg/sentry/fs/inode_overlay.go @@ -34,20 +34,23 @@ func overlayCreateWhiteout(parent *Inode, name string) error { } func overlayWriteOut(ctx context.Context, o *overlayEntry) error { + // Hot path. Avoid defers. + var err error o.copyMu.RLock() - defer o.copyMu.RUnlock() - if o.upper == nil { - return nil + if o.upper != nil { + err = o.upper.InodeOperations.WriteOut(ctx, o.upper) } - return o.upper.InodeOperations.WriteOut(ctx, o.upper) + o.copyMu.RUnlock() + return err } func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name string) (*Dirent, error) { + // Hot path. Avoid defers. parent.copyMu.RLock() - defer parent.copyMu.RUnlock() // Assert that there is at least one upper or lower entry. if parent.upper == nil && parent.lower == nil { + parent.copyMu.RUnlock() panic("invalid overlayEntry, needs at least one Inode") } @@ -63,30 +66,33 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name if err != nil && err != syserror.ENOENT { // We encountered an error that an overlay cannot handle, // we must propagate it to the caller. + parent.copyMu.RUnlock() return nil, err } if child != nil { - defer child.DecRef() - - // Is the child non-negative? if !child.IsNegative() { upperInode = child.Inode upperInode.IncRef() } + child.DecRef() } // Are we done? if overlayHasWhiteout(parent.upper, name) { if upperInode == nil { + parent.copyMu.RUnlock() return NewNegativeDirent(name), nil } entry, err := newOverlayEntry(ctx, upperInode, nil, false) if err != nil { // Don't leak resources. upperInode.DecRef() + parent.copyMu.RUnlock() return nil, err } - return NewDirent(newOverlayInode(ctx, entry, inode.MountSource), name), nil + d, err := NewDirent(newOverlayInode(ctx, entry, inode.MountSource), name), nil + parent.copyMu.RUnlock() + return d, err } } @@ -103,12 +109,10 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name if upperInode != nil { upperInode.DecRef() } + parent.copyMu.RUnlock() return nil, err } if child != nil { - defer child.DecRef() - - // Is the child negative? if !child.IsNegative() { // Did we find something in the upper filesystem? We can // only use it if the types match. @@ -117,12 +121,14 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name lowerInode.IncRef() } } + child.DecRef() } } // Was all of this for naught? if upperInode == nil && lowerInode == nil { // Return a negative Dirent indicating that nothing was found. + parent.copyMu.RUnlock() return NewNegativeDirent(name), nil } @@ -157,9 +163,12 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name if lowerInode != nil { lowerInode.DecRef() } + parent.copyMu.RUnlock() return nil, err } - return NewDirent(newOverlayInode(ctx, entry, inode.MountSource), name), nil + d, err := NewDirent(newOverlayInode(ctx, entry, inode.MountSource), name), nil + parent.copyMu.RUnlock() + return d, err } func overlayCreate(ctx context.Context, o *overlayEntry, parent *Dirent, name string, flags FileFlags, perm FilePermissions) (*File, error) { @@ -349,6 +358,7 @@ func overlayBoundEndpoint(o *overlayEntry, path string) unix.BoundEndpoint { } func overlayGetFile(ctx context.Context, o *overlayEntry, d *Dirent, flags FileFlags) (*File, error) { + // Hot path. Avoid defers. if flags.Write { if err := copyUp(ctx, d); err != nil { return nil, err @@ -356,48 +366,69 @@ func overlayGetFile(ctx context.Context, o *overlayEntry, d *Dirent, flags FileF } o.copyMu.RLock() - defer o.copyMu.RUnlock() if o.upper != nil { upper, err := overlayFile(ctx, o.upper, flags) if err != nil { + o.copyMu.RUnlock() return nil, err } flags.Pread = upper.Flags().Pread flags.Pwrite = upper.Flags().Pwrite - return NewFile(ctx, d, flags, &overlayFileOperations{upper: upper}), nil + f, err := NewFile(ctx, d, flags, &overlayFileOperations{upper: upper}), nil + o.copyMu.RUnlock() + return f, err } lower, err := overlayFile(ctx, o.lower, flags) if err != nil { + o.copyMu.RUnlock() return nil, err } flags.Pread = lower.Flags().Pread flags.Pwrite = lower.Flags().Pwrite + o.copyMu.RUnlock() return NewFile(ctx, d, flags, &overlayFileOperations{lower: lower}), nil } func overlayUnstableAttr(ctx context.Context, o *overlayEntry) (UnstableAttr, error) { + // Hot path. Avoid defers. + var ( + attr UnstableAttr + err error + ) o.copyMu.RLock() - defer o.copyMu.RUnlock() if o.upper != nil { - return o.upper.UnstableAttr(ctx) + attr, err = o.upper.UnstableAttr(ctx) + } else { + attr, err = o.lower.UnstableAttr(ctx) } - return o.lower.UnstableAttr(ctx) + o.copyMu.RUnlock() + return attr, err } func overlayGetxattr(o *overlayEntry, name string) ([]byte, error) { + // Hot path. This is how the overlay checks for whiteout files. + // Avoid defers. + var ( + b []byte + err error + ) + // Don't forward the value of the extended attribute if it would // unexpectedly change the behavior of a wrapping overlay layer. if strings.HasPrefix(XattrOverlayPrefix, name) { return nil, syserror.ENODATA } + o.copyMu.RLock() - defer o.copyMu.RUnlock() if o.upper != nil { - return o.upper.Getxattr(name) + b, err = o.upper.Getxattr(name) + } else { + b, err = o.lower.Getxattr(name) } - return o.lower.Getxattr(name) + o.copyMu.RUnlock() + return b, err } func overlayListxattr(o *overlayEntry) (map[string]struct{}, error) { @@ -422,17 +453,21 @@ func overlayListxattr(o *overlayEntry) (map[string]struct{}, error) { func overlayCheck(ctx context.Context, o *overlayEntry, p PermMask) error { o.copyMu.RLock() - defer o.copyMu.RUnlock() + // Hot path. Avoid defers. + var err error if o.upper != nil { - return o.upper.check(ctx, p) - } - if p.Write { - // Since writes will be redirected to the upper filesystem, the lower - // filesystem need not be writable, but must be readable for copy-up. - p.Write = false - p.Read = true + err = o.upper.check(ctx, p) + } else { + if p.Write { + // Since writes will be redirected to the upper filesystem, the lower + // filesystem need not be writable, but must be readable for copy-up. + p.Write = false + p.Read = true + } + err = o.lower.check(ctx, p) } - return o.lower.check(ctx, p) + o.copyMu.RUnlock() + return err } func overlaySetPermissions(ctx context.Context, o *overlayEntry, d *Dirent, f FilePermissions) bool { @@ -520,12 +555,16 @@ func overlayStatFS(ctx context.Context, o *overlayEntry) (Info, error) { } func overlayHandleOps(o *overlayEntry) HandleOperations { + // Hot path. Avoid defers. + var hops HandleOperations o.copyMu.RLock() - defer o.copyMu.RUnlock() if o.upper != nil { - return o.upper.HandleOps() + hops = o.upper.HandleOps() + } else { + hops = o.lower.HandleOps() } - return o.lower.HandleOps() + o.copyMu.RUnlock() + return hops } // NewTestOverlayDir returns an overlay Inode for tests. diff --git a/pkg/sentry/fs/ramfs/ramfs.go b/pkg/sentry/fs/ramfs/ramfs.go index 04f2d38de..90b6c9a4f 100644 --- a/pkg/sentry/fs/ramfs/ramfs.go +++ b/pkg/sentry/fs/ramfs/ramfs.go @@ -95,8 +95,9 @@ func (e *Entry) InitEntryWithAttr(ctx context.Context, uattr fs.UnstableAttr) { // UnstableAttr implements fs.InodeOperations.UnstableAttr. func (e *Entry) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) { e.mu.Lock() - defer e.mu.Unlock() - return e.unstable, nil + attr := e.unstable + e.mu.Unlock() + return attr, nil } // Check implements fs.InodeOperations.Check. @@ -106,9 +107,11 @@ func (*Entry) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool { // Getxattr implements fs.InodeOperations.Getxattr. func (e *Entry) Getxattr(inode *fs.Inode, name string) ([]byte, error) { + // Hot path. Avoid defers. e.mu.Lock() - defer e.mu.Unlock() - if value, ok := e.xattrs[name]; ok { + value, ok := e.xattrs[name] + e.mu.Unlock() + if ok { return value, nil } return nil, syserror.ENOATTR @@ -117,19 +120,19 @@ func (e *Entry) Getxattr(inode *fs.Inode, name string) ([]byte, error) { // Setxattr implements fs.InodeOperations.Setxattr. func (e *Entry) Setxattr(inode *fs.Inode, name string, value []byte) error { e.mu.Lock() - defer e.mu.Unlock() e.xattrs[name] = value + e.mu.Unlock() return nil } // Listxattr implements fs.InodeOperations.Listxattr. func (e *Entry) Listxattr(inode *fs.Inode) (map[string]struct{}, error) { e.mu.Lock() - defer e.mu.Unlock() names := make(map[string]struct{}, len(e.xattrs)) for name := range e.xattrs { names[name] = struct{}{} } + e.mu.Unlock() return names, nil } @@ -141,22 +144,22 @@ func (*Entry) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*f // SetPermissions always sets the permissions. func (e *Entry) SetPermissions(ctx context.Context, inode *fs.Inode, p fs.FilePermissions) bool { e.mu.Lock() - defer e.mu.Unlock() e.unstable.Perms = p e.unstable.StatusChangeTime = ktime.NowFromContext(ctx) + e.mu.Unlock() return true } // SetOwner always sets ownership. func (e *Entry) SetOwner(ctx context.Context, inode *fs.Inode, owner fs.FileOwner) error { e.mu.Lock() - defer e.mu.Unlock() if owner.UID.Ok() { e.unstable.Owner.UID = owner.UID } if owner.GID.Ok() { e.unstable.Owner.GID = owner.GID } + e.mu.Unlock() return nil } @@ -167,8 +170,6 @@ func (e *Entry) SetTimestamps(ctx context.Context, inode *fs.Inode, ts fs.TimeSp } e.mu.Lock() - defer e.mu.Unlock() - now := ktime.NowFromContext(ctx) if !ts.ATimeOmit { if ts.ATimeSetSystemTime { @@ -185,59 +186,64 @@ func (e *Entry) SetTimestamps(ctx context.Context, inode *fs.Inode, ts fs.TimeSp } } e.unstable.StatusChangeTime = now + e.mu.Unlock() return nil } // NotifyStatusChange updates the status change time (ctime). func (e *Entry) NotifyStatusChange(ctx context.Context) { e.mu.Lock() - defer e.mu.Unlock() e.unstable.StatusChangeTime = ktime.NowFromContext(ctx) + e.mu.Unlock() } // StatusChangeTime returns the last status change time for this node. func (e *Entry) StatusChangeTime() ktime.Time { e.mu.Lock() - defer e.mu.Unlock() - return e.unstable.StatusChangeTime + t := e.unstable.StatusChangeTime + e.mu.Unlock() + return t } // NotifyModification updates the modification time and the status change time. func (e *Entry) NotifyModification(ctx context.Context) { e.mu.Lock() - defer e.mu.Unlock() now := ktime.NowFromContext(ctx) e.unstable.ModificationTime = now e.unstable.StatusChangeTime = now + e.mu.Unlock() } // ModificationTime returns the last modification time for this node. func (e *Entry) ModificationTime() ktime.Time { e.mu.Lock() - defer e.mu.Unlock() - return e.unstable.ModificationTime + t := e.unstable.ModificationTime + e.mu.Unlock() + return t } // NotifyAccess updates the access time. func (e *Entry) NotifyAccess(ctx context.Context) { e.mu.Lock() - defer e.mu.Unlock() now := ktime.NowFromContext(ctx) e.unstable.AccessTime = now + e.mu.Unlock() } // AccessTime returns the last access time for this node. func (e *Entry) AccessTime() ktime.Time { e.mu.Lock() - defer e.mu.Unlock() - return e.unstable.AccessTime + t := e.unstable.AccessTime + e.mu.Unlock() + return t } // Permissions returns permissions on this entry. func (e *Entry) Permissions() fs.FilePermissions { e.mu.Lock() - defer e.mu.Unlock() - return e.unstable.Perms + p := e.unstable.Perms + e.mu.Unlock() + return p } // Lookup is not supported by default. @@ -379,15 +385,15 @@ func (e *Entry) Release(context.Context) {} // AddLink implements InodeOperationss.AddLink. func (e *Entry) AddLink() { e.mu.Lock() - defer e.mu.Unlock() e.unstable.Links++ + e.mu.Unlock() } // DropLink implements InodeOperationss.DropLink. func (e *Entry) DropLink() { e.mu.Lock() - defer e.mu.Unlock() e.unstable.Links-- + e.mu.Unlock() } // DeprecatedReaddir is not supported by default. -- cgit v1.2.3