Commit 15a8bb02 authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher Committed by Han-Wen Nienhuys

fuse: add `SyncRead` flag to MountOptions to ensure in-order reads

We enable FUSE_CAP_ASYNC_READ per default, which means that
the kernel can (and does) submit multiple concurrent out-of-order
read requests to service userspace reads and kernel readahead.

For some backing storages, like Amazon Cloud Drive, out-of-order
reads are expensive.

gocryptfs has implemented a delay-based workaround with its
`-serialize_reads` flag for this case
(see https://github.com/rfjakob/gocryptfs/issues/92 for details).

Not enabling FUSE_CAP_ASYNC_READ makes the kernel do this for us,
as verified by adding debug output to gocryptfs, so expose it as
a mount flag in MountOptions.

Fixes: https://github.com/hanwen/go-fuse/issues/140
Graphs-at: https://github.com/hanwen/go-fuse/issues/395
Related: https://github.com/rfjakob/gocryptfs/issues/92
Change-Id: I10f947d71e1453989c4a9b66fbb0407f7163994f
parent 0aaef6dd
...@@ -165,6 +165,23 @@ type MountOptions struct { ...@@ -165,6 +165,23 @@ type MountOptions struct {
// The filesystem is fully responsible for invalidating data cache. // The filesystem is fully responsible for invalidating data cache.
ExplicitDataCacheControl bool ExplicitDataCacheControl bool
// SyncRead is off by default, which means that go-fuse enable the
// FUSE_CAP_ASYNC_READ capability.
// The kernel then submits multiple concurrent reads to service
// userspace requests and kernel readahead.
//
// Setting SyncRead disables the FUSE_CAP_ASYNC_READ capability.
// The kernel then only sends one read request per file handle at a time,
// and orders the requests by offset.
//
// This is useful if reading out of order or concurrently is expensive for
// (example: Amazon Cloud Drive).
//
// See the comment to FUSE_CAP_ASYNC_READ in
// https://github.com/libfuse/libfuse/blob/master/include/fuse_common.h
// for more details.
SyncRead bool
// If set, fuse will first attempt to use syscall.Mount instead of // If set, fuse will first attempt to use syscall.Mount instead of
// fusermount to mount the filesystem. This will not update /etc/mtab // fusermount to mount the filesystem. This will not update /etc/mtab
// but might be needed if fusermount is not available. // but might be needed if fusermount is not available.
......
...@@ -98,6 +98,10 @@ func doInit(server *Server, req *request) { ...@@ -98,6 +98,10 @@ func doInit(server *Server, req *request) {
if server.opts.EnableAcl { if server.opts.EnableAcl {
server.kernelSettings.Flags |= CAP_POSIX_ACL server.kernelSettings.Flags |= CAP_POSIX_ACL
} }
if server.opts.SyncRead {
// Clear CAP_ASYNC_READ
server.kernelSettings.Flags &= ^uint32(CAP_ASYNC_READ)
}
dataCacheMode := input.Flags & CAP_AUTO_INVAL_DATA dataCacheMode := input.Flags & CAP_AUTO_INVAL_DATA
if server.opts.ExplicitDataCacheControl { if server.opts.ExplicitDataCacheControl {
......
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