From 7c9234df419acb24d5aeadc17151dbf4d21aadb0 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 23 Nov 2016 14:09:26 -0800 Subject: [PATCH 1/6] Use reusable read buffer to improve performance On my simple, sample filesystem, this speeds up my benchmark (a series of git clones) by a factor of 5x-10x. The vast majority of the time savings is from reduced allocation and GC of giant buffers. Some representative runtime.MemStats fields after a run, before: TotalAlloc=302219702256 Mallocs=343942 Frees=342827 GCCPUFraction=0.199405 After: TotalAlloc=42925688 Mallocs=194497 Frees=193819 GCCPUFraction=0.000480 Note that ReadRequest is normally called from exactly one goroutine, as part of a loop in Server.serve, so switching rio to a sync.Mutex will actually help performance (sync.RWMutex has non-trivial additional overhead). It also makes the code simpler and easier to reason about. This does introduce expensive copies for large buffers. I experimented with having three tiers of buffer sizes instead. In that scenario, small buffers are pooled (as in this commit), medium buffers are allocated and the data copied over (as in this commit), but very large buffers take over c.readbuf instead of copying it, and allocate a new c.readbuf in its place. In my experiments, in order for it to be worth allocating a full sized buffer, the cutoff to count as "very large" has to be very large indeed, and I don't see any evidence that such messages are common enough to warrant the extra code (if indeed such messages exist in practice at all). --- fuse.go | 58 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/fuse.go b/fuse.go index 6db0ef29..133d8135 100644 --- a/fuse.go +++ b/fuse.go @@ -124,9 +124,10 @@ type Conn struct { // File handle for kernel communication. Only safe to access if // rio or wio is held. - dev *os.File - wio sync.RWMutex - rio sync.RWMutex + dev *os.File + wio sync.RWMutex + rio sync.Mutex + readbuf []byte // large buffer for reading kernel requests; guarded by rio // Protocol version negotiated with InitRequest/InitResponse. proto Protocol @@ -166,7 +167,8 @@ func Mount(dir string, options ...MountOption) (*Conn, error) { ready := make(chan struct{}, 1) c := &Conn{ - Ready: ready, + Ready: ready, + readbuf: make([]byte, maxBufSize), } f, err := mount(dir, &conf, ready, &c.MountError) if err != nil { @@ -406,7 +408,9 @@ func (h *Header) RespondError(err error) { // All requests read from the kernel, without data, are shorter than // this. var maxRequestSize = syscall.Getpagesize() -var bufSize = maxRequestSize + maxWrite +var maxBufSize = maxRequestSize + maxWrite + +const bufSize = 128 // 128 bytes is enough for the vast majority of messages. // reqPool is a pool of messages. // @@ -420,12 +424,16 @@ var reqPool = sync.Pool{ New: allocMessage, } -func allocMessage() interface{} { - m := &message{buf: make([]byte, bufSize)} +func makeMessage(n int) *message { + m := &message{buf: make([]byte, n)} m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0])) return m } +func allocMessage() interface{} { + return makeMessage(bufSize) +} + func getMessage(c *Conn) *message { m := reqPool.Get().(*message) m.conn = c @@ -433,6 +441,12 @@ func getMessage(c *Conn) *message { } func putMessage(m *message) { + // Don't save messages with giant buffers. + // It just creates memory pressure, which triggers GC activity, + // which then empties the sync.Pool. + if cap(m.buf) < bufSize || cap(m.buf) > bufSize*2 { + return + } m.buf = m.buf[:bufSize] m.conn = nil m.off = 0 @@ -546,16 +560,28 @@ func (c *Conn) Protocol() Protocol { // Caller must call either Request.Respond or Request.RespondError in // a reasonable time. Caller must not retain Request after that call. func (c *Conn) ReadRequest() (Request, error) { - m := getMessage(c) -loop: - c.rio.RLock() - n, err := syscall.Read(c.fd(), m.buf) - c.rio.RUnlock() - if err == syscall.EINTR { - // OSXFUSE sends EINTR to userspace when a request interrupt - // completed before it got sent to userspace? - goto loop + var m *message + var n int + var err error + c.rio.Lock() + for m == nil { + n, err = syscall.Read(c.fd(), c.readbuf) + if err == syscall.EINTR { + // OSXFUSE sends EINTR to userspace when a request interrupt + // completed before it got sent to userspace? + continue + } + if n <= bufSize { + m = getMessage(c) + } else { + m = makeMessage(n) + m.conn = c + } + if n > 0 { + copy(m.buf, c.readbuf[:n]) + } } + c.rio.Unlock() if err != nil && err != syscall.ENODEV { putMessage(m) return nil, err From 19010702e647c2b03de6d1aa7d06c0a5fe1f1664 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 23 Nov 2016 14:15:44 -0800 Subject: [PATCH 2/6] Migrate from golang.org/x/context to stdlib context In addition to using the final long-term home of the context package, this helps performance. Surprisingly, context.WithCancel showed up in double-digits during cpu profiling. The golang.org/x/net context implementation just calls through to the stdlib context. Eliminating that extra hop halved the time spent in context.WithCancel. --- examples/clockfs/clockfs.go | 2 +- examples/hellofs/hello.go | 2 +- fs/bench/bench_create_test.go | 2 +- fs/bench/bench_lookup_test.go | 3 +-- fs/bench/bench_readwrite_test.go | 2 +- fs/fstestutil/record/record.go | 2 +- fs/fstestutil/record/wait.go | 2 +- fs/fstestutil/testfs.go | 2 +- fs/serve.go | 3 +-- fs/serve_test.go | 2 +- fs/tree.go | 3 +-- options_daemon_timeout_test.go | 2 +- options_test.go | 2 +- 13 files changed, 13 insertions(+), 16 deletions(-) diff --git a/examples/clockfs/clockfs.go b/examples/clockfs/clockfs.go index c053f025..bd72fa17 100644 --- a/examples/clockfs/clockfs.go +++ b/examples/clockfs/clockfs.go @@ -3,6 +3,7 @@ package main import ( + "context" "flag" "fmt" "log" @@ -15,7 +16,6 @@ import ( "bazil.org/fuse/fs" _ "bazil.org/fuse/fs/fstestutil" "bazil.org/fuse/fuseutil" - "golang.org/x/net/context" ) func usage() { diff --git a/examples/hellofs/hello.go b/examples/hellofs/hello.go index eb6a234f..1d0c101e 100644 --- a/examples/hellofs/hello.go +++ b/examples/hellofs/hello.go @@ -2,6 +2,7 @@ package main import ( + "context" "flag" "fmt" "log" @@ -10,7 +11,6 @@ import ( "bazil.org/fuse" "bazil.org/fuse/fs" _ "bazil.org/fuse/fs/fstestutil" - "golang.org/x/net/context" ) func usage() { diff --git a/fs/bench/bench_create_test.go b/fs/bench/bench_create_test.go index 1f78786b..9b3d423c 100644 --- a/fs/bench/bench_create_test.go +++ b/fs/bench/bench_create_test.go @@ -1,6 +1,7 @@ package bench_test import ( + "context" "fmt" "os" "testing" @@ -8,7 +9,6 @@ import ( "bazil.org/fuse" "bazil.org/fuse/fs" "bazil.org/fuse/fs/fstestutil" - "golang.org/x/net/context" ) type dummyFile struct { diff --git a/fs/bench/bench_lookup_test.go b/fs/bench/bench_lookup_test.go index b22edc82..8f704edc 100644 --- a/fs/bench/bench_lookup_test.go +++ b/fs/bench/bench_lookup_test.go @@ -1,11 +1,10 @@ package bench_test import ( + "context" "os" "testing" - "golang.org/x/net/context" - "bazil.org/fuse" "bazil.org/fuse/fs" "bazil.org/fuse/fs/fstestutil" diff --git a/fs/bench/bench_readwrite_test.go b/fs/bench/bench_readwrite_test.go index d26f82c1..361511c1 100644 --- a/fs/bench/bench_readwrite_test.go +++ b/fs/bench/bench_readwrite_test.go @@ -1,6 +1,7 @@ package bench_test import ( + "context" "io" "io/ioutil" "os" @@ -10,7 +11,6 @@ import ( "bazil.org/fuse" "bazil.org/fuse/fs" "bazil.org/fuse/fs/fstestutil" - "golang.org/x/net/context" ) type benchConfig struct { diff --git a/fs/fstestutil/record/record.go b/fs/fstestutil/record/record.go index 13654dee..1777f599 100644 --- a/fs/fstestutil/record/record.go +++ b/fs/fstestutil/record/record.go @@ -1,12 +1,12 @@ package record // import "bazil.org/fuse/fs/fstestutil/record" import ( + "context" "sync" "sync/atomic" "bazil.org/fuse" "bazil.org/fuse/fs" - "golang.org/x/net/context" ) // Writes gathers data from FUSE Write calls. diff --git a/fs/fstestutil/record/wait.go b/fs/fstestutil/record/wait.go index fb151297..2578dc9f 100644 --- a/fs/fstestutil/record/wait.go +++ b/fs/fstestutil/record/wait.go @@ -1,12 +1,12 @@ package record import ( + "context" "sync" "time" "bazil.org/fuse" "bazil.org/fuse/fs" - "golang.org/x/net/context" ) type nothing struct{} diff --git a/fs/fstestutil/testfs.go b/fs/fstestutil/testfs.go index c1988bf7..2cc30a06 100644 --- a/fs/fstestutil/testfs.go +++ b/fs/fstestutil/testfs.go @@ -1,11 +1,11 @@ package fstestutil import ( + "context" "os" "bazil.org/fuse" "bazil.org/fuse/fs" - "golang.org/x/net/context" ) // SimpleFS is a trivial FS that just implements the Root method. diff --git a/fs/serve.go b/fs/serve.go index e9fc5659..58377553 100644 --- a/fs/serve.go +++ b/fs/serve.go @@ -3,6 +3,7 @@ package fs // import "bazil.org/fuse/fs" import ( + "context" "encoding/binary" "fmt" "hash/fnv" @@ -13,8 +14,6 @@ import ( "strings" "sync" "time" - - "golang.org/x/net/context" ) import ( diff --git a/fs/serve_test.go b/fs/serve_test.go index e7f9cfb0..bb72f6f7 100644 --- a/fs/serve_test.go +++ b/fs/serve_test.go @@ -2,6 +2,7 @@ package fs_test import ( "bytes" + "context" "errors" "io" "io/ioutil" @@ -22,7 +23,6 @@ import ( "bazil.org/fuse/fs/fstestutil/record" "bazil.org/fuse/fuseutil" "bazil.org/fuse/syscallx" - "golang.org/x/net/context" ) // TO TEST: diff --git a/fs/tree.go b/fs/tree.go index 7e078045..963e55e5 100644 --- a/fs/tree.go +++ b/fs/tree.go @@ -3,11 +3,10 @@ package fs import ( + "context" "os" pathpkg "path" "strings" - - "golang.org/x/net/context" ) import ( diff --git a/options_daemon_timeout_test.go b/options_daemon_timeout_test.go index 1cd3eccf..bffb1169 100644 --- a/options_daemon_timeout_test.go +++ b/options_daemon_timeout_test.go @@ -5,6 +5,7 @@ package fuse_test import ( + "context" "os" "runtime" "syscall" @@ -14,7 +15,6 @@ import ( "bazil.org/fuse" "bazil.org/fuse/fs" "bazil.org/fuse/fs/fstestutil" - "golang.org/x/net/context" ) type slowCreaterDir struct { diff --git a/options_test.go b/options_test.go index 965c00c9..c685af54 100644 --- a/options_test.go +++ b/options_test.go @@ -1,6 +1,7 @@ package fuse_test import ( + "context" "os" "runtime" "syscall" @@ -9,7 +10,6 @@ import ( "bazil.org/fuse" "bazil.org/fuse/fs" "bazil.org/fuse/fs/fstestutil" - "golang.org/x/net/context" ) func init() { From 581c2a53ef4cb5efd508d65e9ac68ce3de0892bd Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 23 Nov 2016 14:53:16 -0800 Subject: [PATCH 3/6] Eliminate serveRequest The Request field was never used. This reduces memory allocations in my simple filesystem tests by about 20%. --- fs/serve.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/fs/serve.go b/fs/serve.go index 58377553..511a71fd 100644 --- a/fs/serve.go +++ b/fs/serve.go @@ -345,7 +345,7 @@ type Config struct { func New(conn *fuse.Conn, config *Config) *Server { s := &Server{ conn: conn, - req: map[fuse.RequestID]*serveRequest{}, + req: map[fuse.RequestID]func(){}, nodeRef: map[Node]fuse.NodeID{}, dynamicInode: GenerateDynamicInode, } @@ -371,7 +371,7 @@ type Server struct { // state, protected by meta meta sync.Mutex - req map[fuse.RequestID]*serveRequest + req map[fuse.RequestID]func() // map request to cancel functions node []*serveNode nodeRef map[Node]fuse.NodeID handle []*serveHandle @@ -436,11 +436,6 @@ func Serve(c *fuse.Conn, fs FS) error { type nothing struct{} -type serveRequest struct { - Request fuse.Request - cancel func() -} - type serveNode struct { inode uint64 generation uint64 @@ -773,8 +768,6 @@ func (c *Server) serve(r fuse.Request) { ctx = c.context(ctx, r) } - req := &serveRequest{Request: r, cancel: cancel} - c.debug(request{ Op: opName(r), Request: r.Hdr(), @@ -813,7 +806,7 @@ func (c *Server) serve(r fuse.Request) { // // TODO this might have been because of missing done() calls } else { - c.req[hdr.ID] = req + c.req[hdr.ID] = cancel } c.meta.Unlock() @@ -1372,10 +1365,9 @@ func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, case *fuse.InterruptRequest: c.meta.Lock() - ireq := c.req[r.IntrID] - if ireq != nil && ireq.cancel != nil { - ireq.cancel() - ireq.cancel = nil + if cancel := c.req[r.IntrID]; cancel != nil { + cancel() + delete(c.req, r.IntrID) } c.meta.Unlock() done(nil) From d9f5d658ebc0a477fd382f9778352fe09e0d62ed Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 23 Nov 2016 15:06:03 -0800 Subject: [PATCH 4/6] Make fuse.Debug nil by default and guard calls to it Eliminates about 20% of allocations in my simple filesystem. --- debug.go | 4 +-- fs/serve.go | 78 ++++++++++++++++++++++++++++++++--------------------- fuse.go | 12 ++++++--- 3 files changed, 56 insertions(+), 38 deletions(-) diff --git a/debug.go b/debug.go index be9f900d..3a5eea72 100644 --- a/debug.go +++ b/debug.go @@ -9,8 +9,6 @@ func stack() string { return string(buf[:runtime.Stack(buf, false)]) } -func nop(msg interface{}) {} - // Debug is called to output debug messages, including protocol // traces. The default behavior is to do nothing. // @@ -18,4 +16,4 @@ func nop(msg interface{}) {} // safe to marshal to JSON. // // Implementations must not retain msg. -var Debug func(msg interface{}) = nop +var Debug func(msg interface{}) diff --git a/fs/serve.go b/fs/serve.go index 511a71fd..c47cdd2e 100644 --- a/fs/serve.go +++ b/fs/serve.go @@ -532,7 +532,9 @@ func (c *Server) dropNode(id fuse.NodeID, n uint64) (forget bool) { // this should only happen if refcounts kernel<->us disagree // *and* two ForgetRequests for the same node race each other; // this indicates a bug somewhere - c.debug(nodeRefcountDropBug{N: n, Node: id}) + if c.debug != nil { + c.debug(nodeRefcountDropBug{N: n, Node: id}) + } // we may end up triggering Forget twice, but that's better // than not even once, and that's the best we can do @@ -540,7 +542,9 @@ func (c *Server) dropNode(id fuse.NodeID, n uint64) (forget bool) { } if n > snode.refs { - c.debug(nodeRefcountDropBug{N: n, Refs: snode.refs, Node: id}) + if c.debug != nil { + c.debug(nodeRefcountDropBug{N: n, Refs: snode.refs, Node: id}) + } n = snode.refs } @@ -579,10 +583,12 @@ func (c *Server) getHandle(id fuse.HandleID) (shandle *serveHandle) { shandle = c.handle[uint(id)] } if shandle == nil { - c.debug(missingHandle{ - Handle: id, - MaxHandle: fuse.HandleID(len(c.handle)), - }) + if c.debug != nil { + c.debug(missingHandle{ + Handle: id, + MaxHandle: fuse.HandleID(len(c.handle)), + }) + } } return } @@ -768,11 +774,13 @@ func (c *Server) serve(r fuse.Request) { ctx = c.context(ctx, r) } - c.debug(request{ - Op: opName(r), - Request: r.Hdr(), - In: r, - }) + if c.debug != nil { + c.debug(request{ + Op: opName(r), + Request: r.Hdr(), + In: r, + }) + } var node Node var snode *serveNode c.meta.Lock() @@ -783,17 +791,19 @@ func (c *Server) serve(r fuse.Request) { } if snode == nil { c.meta.Unlock() - c.debug(response{ - Op: opName(r), - Request: logResponseHeader{ID: hdr.ID}, - Error: fuse.ESTALE.ErrnoName(), - // this is the only place that sets both Error and - // Out; not sure if i want to do that; might get rid - // of len(c.node) things altogether - Out: logMissingNode{ - MaxNode: fuse.NodeID(len(c.node)), - }, - }) + if c.debug != nil { + c.debug(response{ + Op: opName(r), + Request: logResponseHeader{ID: hdr.ID}, + Error: fuse.ESTALE.ErrnoName(), + // this is the only place that sets both Error and + // Out; not sure if i want to do that; might get rid + // of len(c.node) things altogether + Out: logMissingNode{ + MaxNode: fuse.NodeID(len(c.node)), + }, + }) + } r.RespondError(fuse.ESTALE) return } @@ -834,7 +844,9 @@ func (c *Server) serve(r fuse.Request) { } else { msg.Out = resp } - c.debug(msg) + if c.debug != nil { + c.debug(msg) + } c.meta.Lock() delete(c.req, hdr.ID) @@ -989,10 +1001,12 @@ func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, } c.meta.Unlock() if oldNode == nil { - c.debug(logLinkRequestOldNodeNotFound{ - Request: r.Hdr(), - In: r, - }) + if c.debug != nil { + c.debug(logLinkRequestOldNodeNotFound{ + Request: r.Hdr(), + In: r, + }) + } return fuse.EIO } n2, err := n.Link(ctx, r, oldNode.node) @@ -1314,10 +1328,12 @@ func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, } c.meta.Unlock() if newDirNode == nil { - c.debug(renameNewDirNodeNotFound{ - Request: r.Hdr(), - In: r, - }) + if c.debug != nil { + c.debug(renameNewDirNodeNotFound{ + Request: r.Hdr(), + In: r, + }) + } return fuse.EIO } n, ok := node.(NodeRenamer) diff --git a/fuse.go b/fuse.go index 133d8135..7e681c97 100644 --- a/fuse.go +++ b/fuse.go @@ -622,7 +622,9 @@ func (c *Conn) ReadRequest() (Request, error) { var req Request switch m.hdr.Opcode { default: - Debug(noOpcode{Opcode: m.hdr.Opcode}) + if Debug != nil { + Debug(noOpcode{Opcode: m.hdr.Opcode}) + } goto unrecognized case opLookup: @@ -1075,7 +1077,9 @@ func (c *Conn) ReadRequest() (Request, error) { return req, nil corrupt: - Debug(malformedMessage{}) + if Debug != nil { + Debug(malformedMessage{}) + } putMessage(m) return nil, fmt.Errorf("fuse: malformed message") @@ -1121,7 +1125,7 @@ func (c *Conn) writeToKernel(msg []byte) error { c.wio.RLock() defer c.wio.RUnlock() nn, err := syscall.Write(c.fd(), msg) - if err == nil && nn != len(msg) { + if err == nil && nn != len(msg) && Debug != nil { Debug(bugShortKernelWrite{ Written: int64(nn), Length: int64(len(msg)), @@ -1133,7 +1137,7 @@ func (c *Conn) writeToKernel(msg []byte) error { } func (c *Conn) respond(msg []byte) { - if err := c.writeToKernel(msg); err != nil { + if err := c.writeToKernel(msg); err != nil && Debug != nil { Debug(bugKernelWriteError{ Error: errorString(err), Stack: stack(), From cdca86dd77aef05da68c7788935c678b546d95a5 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 23 Nov 2016 15:29:09 -0800 Subject: [PATCH 5/6] Cull dead code buffer.reset is unused. --- buffer.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/buffer.go b/buffer.go index bb1d2b77..5635a898 100644 --- a/buffer.go +++ b/buffer.go @@ -20,14 +20,6 @@ func (w *buffer) alloc(size uintptr) unsafe.Pointer { return unsafe.Pointer(&(*w)[l]) } -// reset clears out the contents of the buffer. -func (w *buffer) reset() { - for i := range (*w)[:cap(*w)] { - (*w)[i] = 0 - } - *w = (*w)[:0] -} - func newBuffer(extra uintptr) buffer { const hdrSize = unsafe.Sizeof(outHeader{}) buf := make(buffer, hdrSize, hdrSize+extra) From f4de1334795191e00ef5073d5d70e508c8eea6a9 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 23 Nov 2016 15:45:38 -0800 Subject: [PATCH 6/6] Allocate response buffer as part of Header Reduces allocations in my simple filesystem by about 25%. Increases the total size of allocations by about 3.5%. Reducing the size of the preallocated buffer moves those numbers in the obvious ways (allocs up, total size down). I picked 160 because it covered > 95% of the messages in my simple filesystem. --- fuse.go | 70 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/fuse.go b/fuse.go index 7e681c97..8c51ea95 100644 --- a/fuse.go +++ b/fuse.go @@ -293,8 +293,8 @@ type Header struct { Gid uint32 // group ID of process making request Pid uint32 // process ID of process making request - // for returning to reqPool - msg *message + msg *message // for returning to reqPool + buf [160]byte // pre-allocated buffer for response; 160 bytes is enough to handle most responses } func (h *Header) String() string { @@ -309,6 +309,14 @@ func (h *Header) noResponse() { putMessage(h.msg) } +func (h *Header) newBuffer(extra uintptr) buffer { + const hdrSize = unsafe.Sizeof(outHeader{}) + if int(hdrSize+extra) < len(h.buf) { + return buffer(h.buf[:hdrSize : hdrSize+extra]) + } + return make(buffer, hdrSize, hdrSize+extra) +} + func (h *Header) respond(msg []byte) { out := (*outHeader)(unsafe.Pointer(&msg[0])) out.Unique = uint64(h.ID) @@ -399,7 +407,7 @@ func (h *Header) RespondError(err error) { } // FUSE uses negative errors! // TODO: File bug report against OSXFUSE: positive error causes kernel panic. - buf := newBuffer(0) + buf := h.newBuffer(0) hOut := (*outHeader)(unsafe.Pointer(&buf[0])) hOut.Error = -int32(errno) h.respond(buf) @@ -1256,7 +1264,7 @@ func (r *InitResponse) String() string { // Respond replies to the request with the given response. func (r *InitRequest) Respond(resp *InitResponse) { - buf := newBuffer(unsafe.Sizeof(initOut{})) + buf := r.newBuffer(unsafe.Sizeof(initOut{})) out := (*initOut)(buf.alloc(unsafe.Sizeof(initOut{}))) out.Major = resp.Library.Major out.Minor = resp.Library.Minor @@ -1285,7 +1293,7 @@ func (r *StatfsRequest) String() string { // Respond replies to the request with the given response. func (r *StatfsRequest) Respond(resp *StatfsResponse) { - buf := newBuffer(unsafe.Sizeof(statfsOut{})) + buf := r.newBuffer(unsafe.Sizeof(statfsOut{})) out := (*statfsOut)(buf.alloc(unsafe.Sizeof(statfsOut{}))) out.St = kstatfs{ Blocks: resp.Blocks, @@ -1337,7 +1345,7 @@ func (r *AccessRequest) String() string { // Respond replies to the request indicating that access is allowed. // To deny access, use RespondError. func (r *AccessRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -1433,7 +1441,7 @@ func (r *GetattrRequest) String() string { // Respond replies to the request with the given response. func (r *GetattrRequest) Respond(resp *GetattrResponse) { size := attrOutSize(r.Header.Conn.proto) - buf := newBuffer(size) + buf := r.newBuffer(size) out := (*attrOut)(buf.alloc(size)) out.AttrValid = uint64(resp.Attr.Valid / time.Second) out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) @@ -1476,12 +1484,12 @@ func (r *GetxattrRequest) String() string { // Respond replies to the request with the given response. func (r *GetxattrRequest) Respond(resp *GetxattrResponse) { if r.Size == 0 { - buf := newBuffer(unsafe.Sizeof(getxattrOut{})) + buf := r.newBuffer(unsafe.Sizeof(getxattrOut{})) out := (*getxattrOut)(buf.alloc(unsafe.Sizeof(getxattrOut{}))) out.Size = uint32(len(resp.Xattr)) r.respond(buf) } else { - buf := newBuffer(uintptr(len(resp.Xattr))) + buf := r.newBuffer(uintptr(len(resp.Xattr))) buf = append(buf, resp.Xattr...) r.respond(buf) } @@ -1512,12 +1520,12 @@ func (r *ListxattrRequest) String() string { // Respond replies to the request with the given response. func (r *ListxattrRequest) Respond(resp *ListxattrResponse) { if r.Size == 0 { - buf := newBuffer(unsafe.Sizeof(getxattrOut{})) + buf := r.newBuffer(unsafe.Sizeof(getxattrOut{})) out := (*getxattrOut)(buf.alloc(unsafe.Sizeof(getxattrOut{}))) out.Size = uint32(len(resp.Xattr)) r.respond(buf) } else { - buf := newBuffer(uintptr(len(resp.Xattr))) + buf := r.newBuffer(uintptr(len(resp.Xattr))) buf = append(buf, resp.Xattr...) r.respond(buf) } @@ -1554,7 +1562,7 @@ func (r *RemovexattrRequest) String() string { // Respond replies to the request, indicating that the attribute was removed. func (r *RemovexattrRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -1599,7 +1607,7 @@ func (r *SetxattrRequest) String() string { // Respond replies to the request, indicating that the extended attribute was set. func (r *SetxattrRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -1618,7 +1626,7 @@ func (r *LookupRequest) String() string { // Respond replies to the request with the given response. func (r *LookupRequest) Respond(resp *LookupResponse) { size := entryOutSize(r.Header.Conn.proto) - buf := newBuffer(size) + buf := r.newBuffer(size) out := (*entryOut)(buf.alloc(size)) out.Nodeid = uint64(resp.Node) out.Generation = resp.Generation @@ -1661,7 +1669,7 @@ func (r *OpenRequest) String() string { // Respond replies to the request with the given response. func (r *OpenRequest) Respond(resp *OpenResponse) { - buf := newBuffer(unsafe.Sizeof(openOut{})) + buf := r.newBuffer(unsafe.Sizeof(openOut{})) out := (*openOut)(buf.alloc(unsafe.Sizeof(openOut{}))) out.Fh = uint64(resp.Handle) out.OpenFlags = uint32(resp.Flags) @@ -1701,7 +1709,7 @@ func (r *CreateRequest) String() string { // Respond replies to the request with the given response. func (r *CreateRequest) Respond(resp *CreateResponse) { eSize := entryOutSize(r.Header.Conn.proto) - buf := newBuffer(eSize + unsafe.Sizeof(openOut{})) + buf := r.newBuffer(eSize + unsafe.Sizeof(openOut{})) e := (*entryOut)(buf.alloc(eSize)) e.Nodeid = uint64(resp.Node) @@ -1748,7 +1756,7 @@ func (r *MkdirRequest) String() string { // Respond replies to the request with the given response. func (r *MkdirRequest) Respond(resp *MkdirResponse) { size := entryOutSize(r.Header.Conn.proto) - buf := newBuffer(size) + buf := r.newBuffer(size) out := (*entryOut)(buf.alloc(size)) out.Nodeid = uint64(resp.Node) out.Generation = resp.Generation @@ -1789,7 +1797,7 @@ func (r *ReadRequest) String() string { // Respond replies to the request with the given response. func (r *ReadRequest) Respond(resp *ReadResponse) { - buf := newBuffer(uintptr(len(resp.Data))) + buf := r.newBuffer(uintptr(len(resp.Data))) buf = append(buf, resp.Data...) r.respond(buf) } @@ -1832,7 +1840,7 @@ func (r *ReleaseRequest) String() string { // Respond replies to the request, indicating that the handle has been released. func (r *ReleaseRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -1851,7 +1859,7 @@ func (r *DestroyRequest) String() string { // Respond replies to the request. func (r *DestroyRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -1991,7 +1999,7 @@ func (r *WriteRequest) MarshalJSON() ([]byte, error) { // Respond replies to the request with the given response. func (r *WriteRequest) Respond(resp *WriteResponse) { - buf := newBuffer(unsafe.Sizeof(writeOut{})) + buf := r.newBuffer(unsafe.Sizeof(writeOut{})) out := (*writeOut)(buf.alloc(unsafe.Sizeof(writeOut{}))) out.Size = uint32(resp.Size) r.respond(buf) @@ -2082,7 +2090,7 @@ func (r *SetattrRequest) String() string { // giving the updated attributes. func (r *SetattrRequest) Respond(resp *SetattrResponse) { size := attrOutSize(r.Header.Conn.proto) - buf := newBuffer(size) + buf := r.newBuffer(size) out := (*attrOut)(buf.alloc(size)) out.AttrValid = uint64(resp.Attr.Valid / time.Second) out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) @@ -2117,7 +2125,7 @@ func (r *FlushRequest) String() string { // Respond replies to the request, indicating that the flush succeeded. func (r *FlushRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -2137,7 +2145,7 @@ func (r *RemoveRequest) String() string { // Respond replies to the request, indicating that the file was removed. func (r *RemoveRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -2156,7 +2164,7 @@ func (r *SymlinkRequest) String() string { // Respond replies to the request, indicating that the symlink was created. func (r *SymlinkRequest) Respond(resp *SymlinkResponse) { size := entryOutSize(r.Header.Conn.proto) - buf := newBuffer(size) + buf := r.newBuffer(size) out := (*entryOut)(buf.alloc(size)) out.Nodeid = uint64(resp.Node) out.Generation = resp.Generation @@ -2189,7 +2197,7 @@ func (r *ReadlinkRequest) String() string { } func (r *ReadlinkRequest) Respond(target string) { - buf := newBuffer(uintptr(len(target))) + buf := r.newBuffer(uintptr(len(target))) buf = append(buf, target...) r.respond(buf) } @@ -2209,7 +2217,7 @@ func (r *LinkRequest) String() string { func (r *LinkRequest) Respond(resp *LookupResponse) { size := entryOutSize(r.Header.Conn.proto) - buf := newBuffer(size) + buf := r.newBuffer(size) out := (*entryOut)(buf.alloc(size)) out.Nodeid = uint64(resp.Node) out.Generation = resp.Generation @@ -2235,7 +2243,7 @@ func (r *RenameRequest) String() string { } func (r *RenameRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -2256,7 +2264,7 @@ func (r *MknodRequest) String() string { func (r *MknodRequest) Respond(resp *LookupResponse) { size := entryOutSize(r.Header.Conn.proto) - buf := newBuffer(size) + buf := r.newBuffer(size) out := (*entryOut)(buf.alloc(size)) out.Nodeid = uint64(resp.Node) out.Generation = resp.Generation @@ -2283,7 +2291,7 @@ func (r *FsyncRequest) String() string { } func (r *FsyncRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) } @@ -2328,6 +2336,6 @@ func (r *ExchangeDataRequest) String() string { } func (r *ExchangeDataRequest) Respond() { - buf := newBuffer(0) + buf := r.newBuffer(0) r.respond(buf) }