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 {

4344

header *contentenc.FileHeader

4445

// go-fuse nodefs.loopbackFile

4546

loopbackFile 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) {

282288

return 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) {

299315

defer wlock.unlock(f.ino)

300316

tlog.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