Commit 0e0d3f3b authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fuse: remove custom BufferPool as tunable option

parent 9a423a77
...@@ -130,9 +130,6 @@ type MountOptions struct { ...@@ -130,9 +130,6 @@ type MountOptions struct {
// interested in security labels. // interested in security labels.
IgnoreSecurityLabels bool // ignoring labels should be provided as a fusermount mount option. IgnoreSecurityLabels bool // ignoring labels should be provided as a fusermount mount option.
// If given, use this buffer pool instead of the global one.
Buffers BufferPool
// If RememberInodes is set, we will never forget inodes. // If RememberInodes is set, we will never forget inodes.
// This may be useful for NFS. // This may be useful for NFS.
RememberInodes bool RememberInodes bool
......
...@@ -9,39 +9,18 @@ import ( ...@@ -9,39 +9,18 @@ import (
"sync" "sync"
) )
// BufferPool implements explicit memory management. It is used for // bufferPool implements explicit memory management. It is used for
// minimizing the GC overhead of communicating with the kernel. // minimizing the GC overhead of communicating with the kernel.
type BufferPool interface { type bufferPool struct {
// AllocBuffer creates a buffer of at least the given size. After use,
// it should be deallocated with FreeBuffer().
AllocBuffer(size uint32) []byte
// FreeBuffer takes back a buffer if it was allocated through
// AllocBuffer. It is not an error to call FreeBuffer() on a slice
// obtained elsewhere.
FreeBuffer(slice []byte)
}
type bufferPoolImpl struct {
lock sync.Mutex lock sync.Mutex
// For each page size multiple a list of slice pointers. // For each page size multiple a list of slice pointers.
buffersBySize []*sync.Pool buffersBySize []*sync.Pool
} }
// NewBufferPool returns a BufferPool implementation that that returns
// slices with capacity of a multiple of page size, which have possibly
// been used, and may contain random contents. When using
// NewBufferPool, file system handlers may not hang on to passed-in
// buffers beyond the handler's return.
func NewBufferPool() BufferPool {
bp := new(bufferPoolImpl)
return bp
}
var pageSize = os.Getpagesize() var pageSize = os.Getpagesize()
func (p *bufferPoolImpl) getPool(pageCount int) *sync.Pool { func (p *bufferPool) getPool(pageCount int) *sync.Pool {
p.lock.Lock() p.lock.Lock()
for len(p.buffersBySize) < pageCount+1 { for len(p.buffersBySize) < pageCount+1 {
p.buffersBySize = append(p.buffersBySize, nil) p.buffersBySize = append(p.buffersBySize, nil)
...@@ -56,7 +35,9 @@ func (p *bufferPoolImpl) getPool(pageCount int) *sync.Pool { ...@@ -56,7 +35,9 @@ func (p *bufferPoolImpl) getPool(pageCount int) *sync.Pool {
return pool return pool
} }
func (p *bufferPoolImpl) AllocBuffer(size uint32) []byte { // AllocBuffer creates a buffer of at least the given size. After use,
// it should be deallocated with FreeBuffer().
func (p *bufferPool) AllocBuffer(size uint32) []byte {
sz := int(size) sz := int(size)
if sz < pageSize { if sz < pageSize {
sz = pageSize sz = pageSize
...@@ -71,7 +52,10 @@ func (p *bufferPoolImpl) AllocBuffer(size uint32) []byte { ...@@ -71,7 +52,10 @@ func (p *bufferPoolImpl) AllocBuffer(size uint32) []byte {
return b[:size] return b[:size]
} }
func (p *bufferPoolImpl) FreeBuffer(slice []byte) { // FreeBuffer takes back a buffer if it was allocated through
// AllocBuffer. It is not an error to call FreeBuffer() on a slice
// obtained elsewhere.
func (p *bufferPool) FreeBuffer(slice []byte) {
if slice == nil { if slice == nil {
return return
} }
......
...@@ -9,11 +9,14 @@ import ( ...@@ -9,11 +9,14 @@ import (
) )
func TestBufferPool(t *testing.T) { func TestBufferPool(t *testing.T) {
bp := NewBufferPool() bp := bufferPool{}
size := 1500 size := 1500
buf := bp.AllocBuffer(uint32(size)) buf1 := bp.AllocBuffer(uint32(size))
if len(buf) != size { if len(buf1) != size {
t.Errorf("Expected buffer of %d bytes, got %d bytes", size, len(buf)) t.Errorf("Expected buffer of %d bytes, got %d bytes", size, len(buf1))
} }
bp.FreeBuffer(buf) bp.FreeBuffer(buf1)
// tried testing to see if we get buf1 back if we ask again,
// but it's not guaranteed and sometimes fails
} }
...@@ -39,6 +39,9 @@ type Server struct { ...@@ -39,6 +39,9 @@ type Server struct {
opts *MountOptions opts *MountOptions
// Pools for []byte
buffers bufferPool
// Pool for request structs. // Pool for request structs.
reqPool sync.Pool reqPool sync.Pool
...@@ -132,9 +135,6 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server ...@@ -132,9 +135,6 @@ func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server
fs = NewLockingRawFileSystem(fs) fs = NewLockingRawFileSystem(fs)
} }
if o.Buffers == nil {
o.Buffers = defaultBufferPool
}
if o.MaxWrite < 0 { if o.MaxWrite < 0 {
o.MaxWrite = 0 o.MaxWrite = 0
} }
...@@ -300,7 +300,7 @@ func (ms *Server) returnRequest(req *request) { ...@@ -300,7 +300,7 @@ func (ms *Server) returnRequest(req *request) {
ms.recordStats(req) ms.recordStats(req)
if req.bufferPoolOutputBuf != nil { if req.bufferPoolOutputBuf != nil {
ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf) ms.buffers.FreeBuffer(req.bufferPoolOutputBuf)
req.bufferPoolOutputBuf = nil req.bufferPoolOutputBuf = nil
} }
...@@ -446,9 +446,9 @@ func (ms *Server) allocOut(req *request, size uint32) []byte { ...@@ -446,9 +446,9 @@ func (ms *Server) allocOut(req *request, size uint32) []byte {
return req.bufferPoolOutputBuf return req.bufferPoolOutputBuf
} }
if req.bufferPoolOutputBuf != nil { if req.bufferPoolOutputBuf != nil {
ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf) ms.buffers.FreeBuffer(req.bufferPoolOutputBuf)
} }
req.bufferPoolOutputBuf = ms.opts.Buffers.AllocBuffer(size) req.bufferPoolOutputBuf = ms.buffers.AllocBuffer(size)
return req.bufferPoolOutputBuf return req.bufferPoolOutputBuf
} }
...@@ -787,12 +787,6 @@ func (in *InitIn) SupportsNotify(notifyType int) bool { ...@@ -787,12 +787,6 @@ func (in *InitIn) SupportsNotify(notifyType int) bool {
return false return false
} }
var defaultBufferPool BufferPool
func init() {
defaultBufferPool = NewBufferPool()
}
// WaitMount waits for the first request to be served. Use this to // WaitMount waits for the first request to be served. Use this to
// avoid racing between accessing the (empty or not yet mounted) // avoid racing between accessing the (empty or not yet mounted)
// mountpoint, and the OS trying to setup the user-space mount. // mountpoint, and the OS trying to setup the user-space mount.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment