From 3371ab70ac1596e043c3a9a4375c407964ef3e6f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:06:23 +1000 Subject: [PATCH 01/18] Redefined the contents of OutMessage. --- internal/buffer/out_message.go | 10 +++++++--- internal/buffer/out_message_darwin.go | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 internal/buffer/out_message_darwin.go diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index 0fe8ef4..2edbc8a 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -21,14 +21,18 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) +// We size out messages to be large enough to hold a header for the response +// plus the largest read that may come in. +const outMessageSize = unsafe.Sizeof(fusekernel.OutHeader{}) + MaxReadSize + // OutMessage provides a mechanism for constructing a single contiguous fuse // message from multiple segments, where the first segment is always a // fusekernel.OutHeader message. // -// Must be created with NewOutMessage. Exception: the zero value has -// Bytes() == nil. +// Must be initialized with Reset. type OutMessage struct { - slice []byte + offset uintptr + storage [outMessageSize]byte } // Create a new buffer whose initial contents are a zeroed fusekernel.OutHeader diff --git a/internal/buffer/out_message_darwin.go b/internal/buffer/out_message_darwin.go new file mode 100644 index 0000000..cf42466 --- /dev/null +++ b/internal/buffer/out_message_darwin.go @@ -0,0 +1,21 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package buffer + +// The maximum read size that we expect to ever see from the kernel, used for +// calculating the size of out messages. +// +// Experimentally determined on OS X. +const MaxReadSize = 1 << 20 From c0e60edb32fce85d444748b0e1a718502fbd5f7e Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:09:27 +1000 Subject: [PATCH 02/18] Defined the new OutMessage API. --- internal/buffer/out_message.go | 58 +++++++++++++++------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index 2edbc8a..966768e 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -15,7 +15,6 @@ package buffer import ( - "reflect" "unsafe" "github.com/jacobsa/fuse/internal/fusekernel" @@ -35,54 +34,47 @@ type OutMessage struct { storage [outMessageSize]byte } -// Create a new buffer whose initial contents are a zeroed fusekernel.OutHeader -// message, and with room enough to grow by extra bytes. -func NewOutMessage(extra uintptr) (b OutMessage) { - const headerSize = unsafe.Sizeof(fusekernel.OutHeader{}) - b.slice = make([]byte, headerSize, headerSize+extra) - return +// Reset the message so that it is ready to be used again. Afterward, the +// contents are solely a zeroed header. +func (m *OutMessage) Reset() { + panic("TODO") } -// Return a pointer to the header at the start of the buffer. +// Return a pointer to the header at the start of the message. func (b *OutMessage) OutHeader() (h *fusekernel.OutHeader) { - sh := (*reflect.SliceHeader)(unsafe.Pointer(&b.slice)) - h = (*fusekernel.OutHeader)(unsafe.Pointer(sh.Data)) - return + panic("TODO") } // Grow the buffer by the supplied number of bytes, returning a pointer to the -// start of the new segment. The sum of the arguments given to Grow must not -// exceed the argument given to New when creating the buffer. +// start of the new segment, which is zeroed. If there is no space left, return +// the nil pointer. func (b *OutMessage) Grow(size uintptr) (p unsafe.Pointer) { - sh := (*reflect.SliceHeader)(unsafe.Pointer(&b.slice)) - p = unsafe.Pointer(sh.Data + uintptr(sh.Len)) - b.slice = b.slice[:len(b.slice)+int(size)] - return + panic("TODO") } -// Equivalent to growing by the length of p, then copying p into the new segment. +// Equivalent to Grow, except the new segment is not zeroed. Use with caution! +func (b *OutMessage) GrowNoZero(size uintptr) (p unsafe.Pointer) { + panic("TODO") +} + +// Equivalent to growing by the length of p, then copying p over the new +// segment. func (b *OutMessage) Append(p []byte) { - sh := reflect.SliceHeader{ - Data: uintptr(b.Grow(uintptr(len(p)))), - Len: len(p), - Cap: len(p), - } - - copy(*(*[]byte)(unsafe.Pointer(&sh)), p) + panic("TODO") } -// Equivalent to growing by the length of s, then copying s into the new segment. +// Equivalent to growing by the length of s, then copying s over the new +// segment. func (b *OutMessage) AppendString(s string) { - sh := reflect.SliceHeader{ - Data: uintptr(b.Grow(uintptr(len(s)))), - Len: len(s), - Cap: len(s), - } + panic("TODO") +} - copy(*(*[]byte)(unsafe.Pointer(&sh)), s) +// Return the current size of the buffer. +func (b *OutMessage) Len() int { + panic("TODO") } // Return a reference to the current contents of the buffer. func (b *OutMessage) Bytes() []byte { - return b.slice + panic("TODO") } From 0becfa3df2d1b28b9285f2880abad9bd69be5e0c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:13:18 +1000 Subject: [PATCH 03/18] Began fixing Connection. --- connection.go | 3 ++- conversions.go | 30 +++++++++++++----------------- freelists.go | 41 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/connection.go b/connection.go index 2d83372..cd07ae0 100644 --- a/connection.go +++ b/connection.go @@ -79,7 +79,8 @@ type Connection struct { cancelFuncs map[uint64]func() // Freelists, serviced by freelists.go. - inMessages freelist.Freelist // GUARDED_BY(mu) + inMessages freelist.Freelist // GUARDED_BY(mu) + outMessages freelist.Freelist // GUARDED_BY(mu) } // State that is maintained for each in-flight op. This is stuffed into the diff --git a/conversions.go b/conversions.go index ee3b97d..6682487 100644 --- a/conversions.go +++ b/conversions.go @@ -387,35 +387,31 @@ func convertInMessage( // Outgoing messages //////////////////////////////////////////////////////////////////////// -// Return the response that should be sent to the kernel. If the op requires no -// response, return a nil response. -func kernelResponse( +// Return the response that should be sent to the kernel, or nil if the op +// requires no response. +func (c *Connection) kernelResponse( fuseID uint64, op interface{}, - opErr error, - protocol fusekernel.Protocol) (msg []byte) { - // If the user replied with an error, create room enough just for the result - // header and fill it in with an error. Otherwise create an appropriate + opErr error) (m *buffer.OutMessage) { + // If the user replied with an error, create a response containing just the + // result header with the error filled in. Otherwise create an appropriate // response. - var b buffer.OutMessage if opErr != nil { - b = buffer.NewOutMessage(0) + m = c.getOutMessage() if errno, ok := opErr.(syscall.Errno); ok { - b.OutHeader().Error = -int32(errno) + m.OutHeader().Error = -int32(errno) } else { - b.OutHeader().Error = -int32(syscall.EIO) + m.OutHeader().Error = -int32(syscall.EIO) } } else { - b = kernelResponseForOp(op, protocol) + m = c.kernelResponseForOp(op) } - msg = b.Bytes() - // Fill in the rest of the header, if a response is required. - if msg != nil { - h := b.OutHeader() + if m != nil { + h := m.OutHeader() h.Unique = fuseID - h.Len = uint32(len(msg)) + h.Len = uint32(m.Len()) } return diff --git a/freelists.go b/freelists.go index cae6eee..368a618 100644 --- a/freelists.go +++ b/freelists.go @@ -20,14 +20,20 @@ import ( "github.com/jacobsa/fuse/internal/buffer" ) +//////////////////////////////////////////////////////////////////////// +// buffer.InMessage +//////////////////////////////////////////////////////////////////////// + // LOCKS_EXCLUDED(c.mu) -func (c *Connection) getInMessage() (m *buffer.InMessage) { +func (c *Connection) getInMessage() (x *buffer.InMessage) { c.mu.Lock() - m = (*buffer.InMessage)(c.inMessages.Get()) - if m == nil { - m = new(buffer.InMessage) - } + x = (*buffer.InMessage)(c.inMessages.Get()) c.mu.Unlock() + + if x == nil { + x = new(buffer.InMessage) + } + return } @@ -37,3 +43,28 @@ func (c *Connection) putInMessage(x *buffer.InMessage) { c.inMessages.Put(unsafe.Pointer(x)) c.mu.Unlock() } + +//////////////////////////////////////////////////////////////////////// +// buffer.OutMessage +//////////////////////////////////////////////////////////////////////// + +// LOCKS_EXCLUDED(c.mu) +func (c *Connection) getOutMessage() (x *buffer.OutMessage) { + c.mu.Lock() + x = (*buffer.OutMessage)(c.outMessages.Get()) + c.mu.Unlock() + + if x == nil { + x = new(buffer.OutMessage) + } + x.Reset() + + return +} + +// LOCKS_EXCLUDED(c.mu) +func (c *Connection) putOutMessage(x *buffer.OutMessage) { + c.mu.Lock() + c.outMessages.Put(unsafe.Pointer(x)) + c.mu.Unlock() +} From 5238806cff94ea6764b8d68de0ff225bbb959d70 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:14:58 +1000 Subject: [PATCH 04/18] Connection.Reply --- connection.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/connection.go b/connection.go index cd07ae0..d35184b 100644 --- a/connection.go +++ b/connection.go @@ -449,17 +449,15 @@ func (c *Connection) Reply(ctx context.Context, opErr error) { c.errorLogger.Printf("%T error: %v", op, opErr) } - // Send the reply to the kernel. - replyMsg := kernelResponse(m.Header().Unique, op, opErr, c.protocol) - if replyMsg != nil { - if err := c.writeMessage(replyMsg); err != nil { - if c.errorLogger != nil { - c.errorLogger.Printf("writeMessage: %v", err) - } + // Send the reply to the kernel, if one is required. + outMsg := c.kernelResponse(m.Header().Unique, op, opErr) + if outMsg != nil { + err := c.writeMessage(outMsg.Bytes()) + c.putOutMessage(outMsg) - return + if err != nil && c.errorLogger != nil { + c.errorLogger.Printf("writeMessage: %v", err) } - } } From 384f32c2a379fa518663c4268ce427283d356b34 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:17:06 +1000 Subject: [PATCH 05/18] Connection.kernelResponseForOp --- conversions.go | 89 +++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/conversions.go b/conversions.go index 6682487..feedf0d 100644 --- a/conversions.go +++ b/conversions.go @@ -419,29 +419,28 @@ func (c *Connection) kernelResponse( // Like kernelResponse, but assumes the user replied with a nil error to the // op. Returns a nil response if no response is required. -func kernelResponseForOp( - op interface{}, - protocol fusekernel.Protocol) (b buffer.OutMessage) { +func (c *Connection) kernelResponseForOp( + op interface{}) (m *buffer.OutMessage) { // Create the appropriate output message switch o := op.(type) { case *fuseops.LookUpInodeOp: - size := fusekernel.EntryOutSize(protocol) - b = buffer.NewOutMessage(size) - out := (*fusekernel.EntryOut)(b.Grow(size)) + size := fusekernel.EntryOutSize(c.protocol) + m = c.getOutMessage() + out := (*fusekernel.EntryOut)(m.Grow(size)) convertChildInodeEntry(&o.Entry, out) case *fuseops.GetInodeAttributesOp: - size := fusekernel.AttrOutSize(protocol) - b = buffer.NewOutMessage(size) - out := (*fusekernel.AttrOut)(b.Grow(size)) + size := fusekernel.AttrOutSize(c.protocol) + m = c.getOutMessage() + out := (*fusekernel.AttrOut)(m.Grow(size)) out.AttrValid, out.AttrValidNsec = convertExpirationTime( o.AttributesExpiration) convertAttributes(o.Inode, &o.Attributes, &out.Attr) case *fuseops.SetInodeAttributesOp: - size := fusekernel.AttrOutSize(protocol) - b = buffer.NewOutMessage(size) - out := (*fusekernel.AttrOut)(b.Grow(size)) + size := fusekernel.AttrOutSize(c.protocol) + m = c.getOutMessage() + out := (*fusekernel.AttrOut)(m.Grow(size)) out.AttrValid, out.AttrValidNsec = convertExpirationTime( o.AttributesExpiration) convertAttributes(o.Inode, &o.Attributes, &out.Attr) @@ -450,85 +449,85 @@ func kernelResponseForOp( // No response. case *fuseops.MkDirOp: - size := fusekernel.EntryOutSize(protocol) - b = buffer.NewOutMessage(size) - out := (*fusekernel.EntryOut)(b.Grow(size)) + size := fusekernel.EntryOutSize(c.protocol) + m = c.getOutMessage() + out := (*fusekernel.EntryOut)(m.Grow(size)) convertChildInodeEntry(&o.Entry, out) case *fuseops.CreateFileOp: - eSize := fusekernel.EntryOutSize(protocol) - b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{})) + eSize := fusekernel.EntryOutSize(c.protocol) + m = c.getOutMessage() - e := (*fusekernel.EntryOut)(b.Grow(eSize)) + e := (*fusekernel.EntryOut)(m.Grow(eSize)) convertChildInodeEntry(&o.Entry, e) - oo := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{}))) + oo := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{}))) oo.Fh = uint64(o.Handle) case *fuseops.CreateSymlinkOp: - size := fusekernel.EntryOutSize(protocol) - b = buffer.NewOutMessage(size) - out := (*fusekernel.EntryOut)(b.Grow(size)) + size := fusekernel.EntryOutSize(c.protocol) + m = c.getOutMessage() + out := (*fusekernel.EntryOut)(m.Grow(size)) convertChildInodeEntry(&o.Entry, out) case *fuseops.RenameOp: - b = buffer.NewOutMessage(0) + m = c.getOutMessage() case *fuseops.RmDirOp: - b = buffer.NewOutMessage(0) + m = c.getOutMessage() case *fuseops.UnlinkOp: - b = buffer.NewOutMessage(0) + m = c.getOutMessage() case *fuseops.OpenDirOp: - b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{})) - out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{}))) + m = c.getOutMessage() + out := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{}))) out.Fh = uint64(o.Handle) case *fuseops.ReadDirOp: - b = buffer.NewOutMessage(uintptr(len(o.Data))) - b.Append(o.Data) + m = c.getOutMessage() + m.Append(o.Data) case *fuseops.ReleaseDirHandleOp: - b = buffer.NewOutMessage(0) + m = c.getOutMessage() case *fuseops.OpenFileOp: - b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{})) - out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{}))) + m = c.getOutMessage() + out := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{}))) out.Fh = uint64(o.Handle) case *fuseops.ReadFileOp: - b = buffer.NewOutMessage(uintptr(len(o.Data))) - b.Append(o.Data) + m = c.getOutMessage() + m.Append(o.Data) case *fuseops.WriteFileOp: - b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.WriteOut{})) - out := (*fusekernel.WriteOut)(b.Grow(unsafe.Sizeof(fusekernel.WriteOut{}))) + m = c.getOutMessage() + out := (*fusekernel.WriteOut)(m.Grow(unsafe.Sizeof(fusekernel.WriteOut{}))) out.Size = uint32(len(o.Data)) case *fuseops.SyncFileOp: - b = buffer.NewOutMessage(0) + m = c.getOutMessage() case *fuseops.FlushFileOp: - b = buffer.NewOutMessage(0) + m = c.getOutMessage() case *fuseops.ReleaseFileHandleOp: - b = buffer.NewOutMessage(0) + m = c.getOutMessage() case *fuseops.ReadSymlinkOp: - b = buffer.NewOutMessage(uintptr(len(o.Target))) - b.AppendString(o.Target) + m = c.getOutMessage() + m.AppendString(o.Target) case *statFSOp: - b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) - b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) + m = c.getOutMessage() + m.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) case *interruptOp: // No response. case *initOp: - b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{})) - out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{}))) + m = c.getOutMessage() + out := (*fusekernel.InitOut)(m.Grow(unsafe.Sizeof(fusekernel.InitOut{}))) out.Major = o.Library.Major out.Minor = o.Library.Minor From 5b3f5df8cc24209985c064ae3d466d11fd025db2 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:17:56 +1000 Subject: [PATCH 06/18] Specify that append methods panic. --- internal/buffer/out_message.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index 966768e..4170bf7 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -58,13 +58,13 @@ func (b *OutMessage) GrowNoZero(size uintptr) (p unsafe.Pointer) { } // Equivalent to growing by the length of p, then copying p over the new -// segment. +// segment. Panics if there is not enough room available. func (b *OutMessage) Append(p []byte) { panic("TODO") } // Equivalent to growing by the length of s, then copying s over the new -// segment. +// segment. Panics if there is not enough room available. func (b *OutMessage) AppendString(s string) { panic("TODO") } From f419bd4578f963932469e5eb32dd6ae2f81199fb Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:19:03 +1000 Subject: [PATCH 07/18] Declared memclr. --- internal/buffer/memclr.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 internal/buffer/memclr.go diff --git a/internal/buffer/memclr.go b/internal/buffer/memclr.go new file mode 100644 index 0000000..927032d --- /dev/null +++ b/internal/buffer/memclr.go @@ -0,0 +1,22 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package buffer + +import "unsafe" + +//go:noescape + +// Zero the n bytes starting at p. +func memclr(p unsafe.Pointer, n uintptr) From 8b845a25ee8f48e35f001593830fc8b7354c5444 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:19:37 +1000 Subject: [PATCH 08/18] Implemented memclr. --- internal/buffer/memclr.s | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 internal/buffer/memclr.s diff --git a/internal/buffer/memclr.s b/internal/buffer/memclr.s new file mode 100644 index 0000000..9f65b4e --- /dev/null +++ b/internal/buffer/memclr.s @@ -0,0 +1,33 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build amd64 arm64 ppc64 ppc64le + +// Assembly code isn't subject to visibility restrictions, so we can jump +// directly into package runtime. +// +// Technique copied from here: +// https://github.com/golang/go/blob/d8c6dac/src/os/signal/sig.s + +#include "textflag.h" + +#ifdef GOARCH_ppc64 +#define JMP BR +#endif +#ifdef GOARCH_ppc64le +#define JMP BR +#endif + +TEXT ·memclr(SB),NOSPLIT,$0-16 + JMP runtime·memclr(SB) From c210aa8a95cb3d879660f62fbf63049d2f7f6664 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:20:50 +1000 Subject: [PATCH 09/18] OutMessage.Reset --- internal/buffer/out_message.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index 4170bf7..b87ae1f 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -20,9 +20,11 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) +const outHeaderSize = unsafe.Sizeof(fusekernel.OutHeader{}) + // We size out messages to be large enough to hold a header for the response // plus the largest read that may come in. -const outMessageSize = unsafe.Sizeof(fusekernel.OutHeader{}) + MaxReadSize +const outMessageSize = outHeaderSize + MaxReadSize // OutMessage provides a mechanism for constructing a single contiguous fuse // message from multiple segments, where the first segment is always a @@ -37,7 +39,8 @@ type OutMessage struct { // Reset the message so that it is ready to be used again. Afterward, the // contents are solely a zeroed header. func (m *OutMessage) Reset() { - panic("TODO") + m.offset = outHeaderSize + memclr(unsafe.Pointer(&m.storage), outHeaderSize) } // Return a pointer to the header at the start of the message. From 114432703b4ab49424a74d763a7191da890c9760 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:23:10 +1000 Subject: [PATCH 10/18] Check alignment. --- internal/buffer/out_message.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index b87ae1f..2fb087f 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -15,6 +15,7 @@ package buffer import ( + "log" "unsafe" "github.com/jacobsa/fuse/internal/fusekernel" @@ -36,6 +37,17 @@ type OutMessage struct { storage [outMessageSize]byte } +// Make sure alignment works out correctly, at least for the header. +func init() { + a := unsafe.Alignof(OutMessage{}) + o := unsafe.Offsetof(OutMessage{}.storage) + e := unsafe.Alignof(fusekernel.OutHeader{}) + + if a%e != 0 || o%e != 0 { + log.Panicf("Bad alignment or offset: %d, %d, need %d", a, o, e) + } +} + // Reset the message so that it is ready to be used again. Afterward, the // contents are solely a zeroed header. func (m *OutMessage) Reset() { From 2e3cba1928452a92bb05117c9da16d68d5074b15 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:23:34 +1000 Subject: [PATCH 11/18] OutMessage.OutHeader --- internal/buffer/out_message.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index 2fb087f..c0427f1 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -57,7 +57,8 @@ func (m *OutMessage) Reset() { // Return a pointer to the header at the start of the message. func (b *OutMessage) OutHeader() (h *fusekernel.OutHeader) { - panic("TODO") + h = (*fusekernel.OutHeader)(unsafe.Pointer(&b.storage)) + return } // Grow the buffer by the supplied number of bytes, returning a pointer to the From 9a55ffcd7d292aa13d1c1376034d47458902a389 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:23:59 +1000 Subject: [PATCH 12/18] OutMessage.Grow --- internal/buffer/out_message.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index c0427f1..e8ab8e0 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -65,7 +65,12 @@ func (b *OutMessage) OutHeader() (h *fusekernel.OutHeader) { // start of the new segment, which is zeroed. If there is no space left, return // the nil pointer. func (b *OutMessage) Grow(size uintptr) (p unsafe.Pointer) { - panic("TODO") + p = b.GrowNoZero(size) + if p != nil { + memclr(p, size) + } + + return } // Equivalent to Grow, except the new segment is not zeroed. Use with caution! From e2aa0ec1be253069e26c26ac05a141835ca468c6 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:24:45 +1000 Subject: [PATCH 13/18] OutMessage.GrowNoZero --- internal/buffer/out_message.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index e8ab8e0..f2dad9f 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -75,7 +75,14 @@ func (b *OutMessage) Grow(size uintptr) (p unsafe.Pointer) { // Equivalent to Grow, except the new segment is not zeroed. Use with caution! func (b *OutMessage) GrowNoZero(size uintptr) (p unsafe.Pointer) { - panic("TODO") + if outMessageSize-b.offset < size { + return + } + + p = unsafe.Pointer(uintptr(unsafe.Pointer(&b.storage)) + b.offset) + b.offset += size + + return } // Equivalent to growing by the length of p, then copying p over the new From 75e2706a4b66896e49ef3f2280bfe24f35ffdf9f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:26:42 +1000 Subject: [PATCH 14/18] Renamed two files. --- internal/buffer/{memclr.go => runtime.go} | 0 internal/buffer/{memclr.s => runtime.s} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename internal/buffer/{memclr.go => runtime.go} (100%) rename internal/buffer/{memclr.s => runtime.s} (100%) diff --git a/internal/buffer/memclr.go b/internal/buffer/runtime.go similarity index 100% rename from internal/buffer/memclr.go rename to internal/buffer/runtime.go diff --git a/internal/buffer/memclr.s b/internal/buffer/runtime.s similarity index 100% rename from internal/buffer/memclr.s rename to internal/buffer/runtime.s From bb88db68e0c1cb51f1cb8bb6e98ce72fffaa4bf1 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:28:21 +1000 Subject: [PATCH 15/18] OutMessage.Append --- internal/buffer/out_message.go | 14 ++++++++++++-- internal/buffer/runtime.go | 5 +++++ internal/buffer/runtime.s | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index f2dad9f..66099bf 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -15,7 +15,9 @@ package buffer import ( + "fmt" "log" + "reflect" "unsafe" "github.com/jacobsa/fuse/internal/fusekernel" @@ -87,8 +89,16 @@ func (b *OutMessage) GrowNoZero(size uintptr) (p unsafe.Pointer) { // Equivalent to growing by the length of p, then copying p over the new // segment. Panics if there is not enough room available. -func (b *OutMessage) Append(p []byte) { - panic("TODO") +func (b *OutMessage) Append(src []byte) { + p := b.GrowNoZero(uintptr(len(src))) + if p == nil { + panic(fmt.Sprintf("Can't grow %d bytes", len(src))) + } + + sh := (*reflect.SliceHeader)(unsafe.Pointer(&src)) + memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len)) + + return } // Equivalent to growing by the length of s, then copying s over the new diff --git a/internal/buffer/runtime.go b/internal/buffer/runtime.go index 927032d..672c8a1 100644 --- a/internal/buffer/runtime.go +++ b/internal/buffer/runtime.go @@ -20,3 +20,8 @@ import "unsafe" // Zero the n bytes starting at p. func memclr(p unsafe.Pointer, n uintptr) + +//go:noescape + +// Copy from src to dst, allowing overlap. +func memmove(dst unsafe.Pointer, src unsafe.Pointer, n uintptr) diff --git a/internal/buffer/runtime.s b/internal/buffer/runtime.s index 9f65b4e..f39415a 100644 --- a/internal/buffer/runtime.s +++ b/internal/buffer/runtime.s @@ -31,3 +31,6 @@ TEXT ·memclr(SB),NOSPLIT,$0-16 JMP runtime·memclr(SB) + +TEXT ·memmove(SB),NOSPLIT,$0-24 + JMP runtime·memmove(SB) From a25c297e419fb01e4aad37f676188922d49bbbb4 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:29:09 +1000 Subject: [PATCH 16/18] OutMessage.AppendString --- internal/buffer/out_message.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index 66099bf..977a1e1 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -103,8 +103,16 @@ func (b *OutMessage) Append(src []byte) { // Equivalent to growing by the length of s, then copying s over the new // segment. Panics if there is not enough room available. -func (b *OutMessage) AppendString(s string) { - panic("TODO") +func (b *OutMessage) AppendString(src string) { + p := b.GrowNoZero(uintptr(len(src))) + if p == nil { + panic(fmt.Sprintf("Can't grow %d bytes", len(src))) + } + + sh := (*reflect.StringHeader)(unsafe.Pointer(&src)) + memmove(p, unsafe.Pointer(sh.Data), uintptr(sh.Len)) + + return } // Return the current size of the buffer. From a1fc133e32a11d876e3dce4ef271d7175ba73e2b Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:29:30 +1000 Subject: [PATCH 17/18] Finished OutMessage. --- internal/buffer/out_message.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/buffer/out_message.go b/internal/buffer/out_message.go index 977a1e1..59b5d42 100644 --- a/internal/buffer/out_message.go +++ b/internal/buffer/out_message.go @@ -117,10 +117,10 @@ func (b *OutMessage) AppendString(src string) { // Return the current size of the buffer. func (b *OutMessage) Len() int { - panic("TODO") + return int(b.offset) } // Return a reference to the current contents of the buffer. func (b *OutMessage) Bytes() []byte { - panic("TODO") + return b.storage[:int(b.offset)] } From 240465913fce9903b2cee888a6e495ae803f48af Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 16:31:59 +1000 Subject: [PATCH 18/18] Fixed the build on Linux. --- internal/buffer/out_message_linux.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 internal/buffer/out_message_linux.go diff --git a/internal/buffer/out_message_linux.go b/internal/buffer/out_message_linux.go new file mode 100644 index 0000000..725f7c2 --- /dev/null +++ b/internal/buffer/out_message_linux.go @@ -0,0 +1,21 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package buffer + +// The maximum read size that we expect to ever see from the kernel, used for +// calculating the size of out messages. +// +// For 4 KiB pages, this is 128 KiB (cf. https://goo.gl/HOiEYo) +const MaxReadSize = 1 << 17