github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/vfs/syncing_file_linux.go (about) 1 // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 // +build linux,!arm 6 7 package vfs 8 9 import ( 10 "sync/atomic" 11 "syscall" 12 ) 13 14 type syncFileRange func(fd int, off int64, n int64, flags int) (err error) 15 16 // sync_file_range depends on both the filesystem, and the broader kernel 17 // support. In particular, Windows Subsystem for Linux does not support 18 // sync_file_range, even when used with ext{2,3,4}. syncRangeSmokeTest performs 19 // a test of of sync_file_range, returning false on ENOSYS, and true otherwise. 20 func syncRangeSmokeTest(fd uintptr, fn syncFileRange) bool { 21 err := fn(int(fd), 0 /* offset */, 0 /* nbytes */, 0 /* flags */) 22 return err != syscall.ENOSYS 23 } 24 25 func isSyncRangeSupported(fd uintptr) bool { 26 var stat syscall.Statfs_t 27 if err := syscall.Fstatfs(int(fd), &stat); err != nil { 28 return false 29 } 30 31 // Whitelist which filesystems we allow using sync_file_range with as some 32 // filesystems treat that syscall as a noop (notably ZFS). A whitelist is 33 // used instead of a blacklist in order to have a more graceful failure mode 34 // in case a filesystem we haven't tested is encountered. Currently only 35 // ext2/3/4 are known to work properly. 36 const extMagic = 0xef53 37 switch stat.Type { 38 case extMagic: 39 return syncRangeSmokeTest(fd, syscall.SyncFileRange) 40 } 41 return false 42 } 43 44 func (f *syncingFile) init() { 45 if f.fd == 0 { 46 return 47 } 48 f.useSyncRange = isSyncRangeSupported(f.fd) 49 if f.useSyncRange { 50 f.syncTo = f.syncToRange 51 } else { 52 f.syncTo = f.syncToFdatasync 53 } 54 } 55 56 func (f *syncingFile) syncData() error { 57 if f.fd == 0 { 58 return f.File.Sync() 59 } 60 return syscall.Fdatasync(int(f.fd)) 61 } 62 63 func (f *syncingFile) syncToFdatasync(_ int64) error { 64 f.ratchetSyncOffset(atomic.LoadInt64(&f.atomic.offset)) 65 return f.syncData() 66 } 67 68 func (f *syncingFile) syncToRange(offset int64) error { 69 const ( 70 waitBefore = 0x1 71 write = 0x2 72 // waitAfter = 0x4 73 ) 74 75 f.ratchetSyncOffset(offset) 76 return syscall.SyncFileRange(int(f.fd), 0, offset, write|waitBefore) 77 }