lib/fs, lib/model: Reduce lock contention on NewCaseFilesystem (fixes #7268) (#7269)

This commit is contained in:
Jakob Borg 2021-01-12 16:22:21 +01:00 committed by GitHub
parent f6fac3e949
commit 8f199e12b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 20 deletions

View File

@ -48,25 +48,35 @@ type fskey struct {
// their cache every now and then.
type caseFilesystemRegistry struct {
fss map[fskey]*caseFilesystem
mut sync.Mutex
mut sync.RWMutex
startCleaner sync.Once
}
func (r *caseFilesystemRegistry) get(fs Filesystem) Filesystem {
r.mut.Lock()
defer r.mut.Unlock()
k := fskey{fs.Type(), fs.URI()}
// Use double locking when getting a caseFs. In the common case it will
// already exist and we take the read lock fast path. If it doesn't, we
// take a write lock and try again.
r.mut.RLock()
caseFs, ok := r.fss[k]
r.mut.RUnlock()
if !ok {
caseFs = &caseFilesystem{
Filesystem: fs,
realCaser: newDefaultRealCaser(fs),
r.mut.Lock()
caseFs, ok = r.fss[k]
if !ok {
caseFs = &caseFilesystem{
Filesystem: fs,
realCaser: newDefaultRealCaser(fs),
}
r.fss[k] = caseFs
r.startCleaner.Do(func() {
go r.cleaner()
})
}
r.fss[k] = caseFs
r.startCleaner.Do(func() {
go r.cleaner()
})
r.mut.Unlock()
}
return caseFs
@ -74,11 +84,11 @@ func (r *caseFilesystemRegistry) get(fs Filesystem) Filesystem {
func (r *caseFilesystemRegistry) cleaner() {
for range time.NewTicker(time.Minute).C {
r.mut.Lock()
r.mut.RLock()
for _, caseFs := range r.fss {
caseFs.dropCache()
}
r.mut.Unlock()
r.mut.RUnlock()
}
}

View File

@ -1790,13 +1790,6 @@ func (m *model) Request(deviceID protocol.DeviceID, folder, name string, blockNo
return nil, protocol.ErrInvalid
}
folderFs := folderCfg.Filesystem()
if err := osutil.TraversesSymlink(folderFs, filepath.Dir(name)); err != nil {
l.Debugf("%v REQ(in) traversal check: %s - %s: %q / %q o=%d s=%d", m, err, deviceID, folder, name, offset, size)
return nil, protocol.ErrNoSuchFile
}
// Restrict parallel requests by connection/device
m.pmut.RLock()
@ -1814,6 +1807,16 @@ func (m *model) Request(deviceID protocol.DeviceID, folder, name string, blockNo
}
}()
// Grab the FS after limiting, as it causes I/O and we want to minimize
// the race time between the symlink check and the read.
folderFs := folderCfg.Filesystem()
if err := osutil.TraversesSymlink(folderFs, filepath.Dir(name)); err != nil {
l.Debugf("%v REQ(in) traversal check: %s - %s: %q / %q o=%d s=%d", m, err, deviceID, folder, name, offset, size)
return nil, protocol.ErrNoSuchFile
}
// Only check temp files if the flag is set, and if we are set to advertise
// the temp indexes.
if fromTemporary && !folderCfg.DisableTempIndexes {