From 2adc057d95002128f7f25fd636e3b72d64e518ad Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 8 Jul 2020 10:27:26 +0100 Subject: [PATCH] vfs: fix very high load caused by slow directory listings In this commit (released in v1.52.0) 6ca7198f mount: fix disappearing cwd problem SetSys was introduced to cache node lookups. Unfortunately taking the vfs.(*Dir) lock in SetSys causes any FUSE operations on a directory to pile up behind slow directory listings. In some situations this leads to very high load. This commit fixes it by using atomic operations to read and write the Sys value make it independent of the lock. See: https://forum.rclone.org/t/high-cpu-load-with-rclone-mount/17604 See: #4104 --- vfs/dir.go | 11 ++++------- vfs/file.go | 10 +++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/vfs/dir.go b/vfs/dir.go index 4cba1375e..a6889eb4c 100644 --- a/vfs/dir.go +++ b/vfs/dir.go @@ -7,6 +7,7 @@ import ( "sort" "strings" "sync" + "sync/atomic" "time" "github.com/pkg/errors" @@ -33,7 +34,7 @@ type Dir struct { read time.Time // time directory entry last read items map[string]Node // directory entries - can be empty but not nil virtual map[string]vState // virtual directory entries - may be nil - sys interface{} // user defined info to be attached here + sys atomic.Value // user defined info to be attached here } //go:generate stringer -type=vState @@ -105,16 +106,12 @@ func (d *Dir) Path() (name string) { // Sys returns underlying data source (can be nil) - satisfies Node interface func (d *Dir) Sys() interface{} { - d.mu.RLock() - defer d.mu.RUnlock() - return d.sys + return d.sys.Load() } // SetSys sets the underlying data source (can be nil) - satisfies Node interface func (d *Dir) SetSys(x interface{}) { - d.mu.Lock() - d.sys = x - d.mu.Unlock() + d.sys.Store(x) } // Inode returns the inode number - satisfies Node interface diff --git a/vfs/file.go b/vfs/file.go index 891c6c1cc..aadfcb3ee 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -48,7 +48,7 @@ type File struct { pendingModTime time.Time // will be applied once o becomes available, i.e. after file was written pendingRenameFun func(ctx context.Context) error // will be run/renamed after all writers close appendMode bool // file was opened with O_APPEND - sys interface{} // user defined info to be attached here + sys atomic.Value // user defined info to be attached here muRW sync.Mutex // synchronize RWFileHandle.openPending(), RWFileHandle.close() and File.Remove } @@ -122,16 +122,12 @@ func (f *File) Path() string { // Sys returns underlying data source (can be nil) - satisfies Node interface func (f *File) Sys() interface{} { - f.mu.RLock() - defer f.mu.RUnlock() - return f.sys + return f.sys.Load() } // SetSys sets the underlying data source (can be nil) - satisfies Node interface func (f *File) SetSys(x interface{}) { - f.mu.Lock() - f.sys = x - f.mu.Unlock() + f.sys.Store(x) } // Inode returns the inode number - satisfies Node interface