fusefronted: optimize NFS streaming writes by saving one Stat() · rfjakob/gocryptfs@a08d55f
@@ -9,6 +9,7 @@ import (
99"log"
1010"os"
1111"sync"
12+"sync/atomic"
1213"syscall"
1314"time"
1415@@ -43,6 +44,11 @@ type file struct {
4344header *contentenc.FileHeader
4445// go-fuse nodefs.loopbackFile
4546loopbackFile nodefs.File
47+// Store what the last byte was written
48+lastWrittenOffset int64
49+// The opCount is used to judge whether "lastWrittenOffset" is still
50+// guaranteed to be correct.
51+lastOpCount uint64
4652}
47534854// NewFile returns a new go-fuse File instance.
@@ -282,6 +288,16 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
282288return written, status
283289}
284290291+// isConsecutiveWrite returns true if the current write
292+// directly (in time and space) follows the last write.
293+// This is an optimisation for streaming writes on NFS where a
294+// Stat() call is very expensive.
295+// The caller must "wlock.lock(f.ino)" otherwise this check would be racy.
296+func (f *file) isConsecutiveWrite(off int64) bool {
297+opCount := atomic.LoadUint64(&wlock.opCount)
298+return opCount == f.lastOpCount+1 && off == f.lastWrittenOffset+1
299+}
300+285301// Write - FUSE call
286302//
287303// If the write creates a hole, pads the file to the next block boundary.
@@ -299,11 +315,20 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {
299315defer wlock.unlock(f.ino)
300316tlog.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.ino, off, len(data))
301317// If the write creates a file hole, we have to zero-pad the last block.
302-status := f.writePadHole(off)
303-if !status.Ok() {
304-return 0, status
318+// But if the write directly follows an earlier write, it cannot create a
319+// hole, and we can save one Stat() call.
320+if !f.isConsecutiveWrite(off) {
321+status := f.writePadHole(off)
322+if !status.Ok() {
323+return 0, status
324+ }
325+ }
326+n, status := f.doWrite(data, off)
327+if status.Ok() {
328+f.lastOpCount = atomic.LoadUint64(&wlock.opCount)
329+f.lastWrittenOffset = off + int64(len(data)) - 1
305330 }
306-return f.doWrite(data, off)
331+return n, status
307332}
308333309334// Release - FUSE call, close file