From ddf6a2f105e65fa82862255f07d451b10fe66ade Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Tue, 28 May 2019 15:15:18 -0400 Subject: [PATCH] make async_read optional again --- README.md | 13 ++++++++----- libfuse/include/fuse_common.h | 5 ----- libfuse/lib/fuse_lowlevel.c | 11 ++--------- man/mergerfs.1 | 20 +++++++++++++++----- src/config.cpp | 8 +++++--- src/config.hpp | 1 + src/fuse_getxattr.cpp | 2 ++ src/fuse_init.cpp | 33 ++++++++++++++++++++------------- src/fuse_listxattr.cpp | 1 + src/option_parser.cpp | 16 +++++++++++++--- 10 files changed, 67 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 2ab09dea..9b93a7f8 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs** * **statfs=base|full**: Controls how statfs works. 'base' means it will always use all branches in statfs calculations. 'full' is in effect path preserving and only includes drives where the path exists. (default: base) * **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to ignore available space for branches mounted or tagged as 'read-only' or 'no create'. 'nc' will ignore available space for branches tagged as 'no create'. (default: none) * **posix_acl=true|false:** enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false) +* **async_read=true|false:** Perform reads asynchronously. If disabled or unavailable the kernel will ensure there is at most one pending read request per file handle and will attempt to order requests by offset. (default: true) * **threads=num**: number of threads to use in multithreaded mode. When set to zero (the default) it will attempt to discover and use the number of logical cores. If the lookup fails it will fall back to using 4. If the thread count is set negative it will look up the number of cores then divide by the absolute value. ie. threads=-2 on an 8 core machine will result in 8 / 2 = 4 threads. There will always be at least 1 thread. NOTE: higher number of threads increases parallelism but usually decreases throughput. (default: number of cores) *NOTE2:* the option is unavailable when built with system libfuse. * **fsname=name**: sets the name of the filesystem as seen in **mount**, **df**, etc. Defaults to a list of the source paths concatenated together with the longest common prefix removed. * **func.<func>=<policy>**: sets the specific FUSE function's policy. See below for the list of value types. Example: **func.getattr=newest** @@ -980,16 +981,18 @@ For non-Linux systems mergerfs uses a read-write lock and changes credentials on NOTE: be sure to read about these features before changing them -* add (or remove) `direct_io` -* add (or remove) `auto_cache` -* add (or remove) `kernel_cache` -* add (or remove) `splice_move`, `splice_read`, and `splice_write` +* enable (or disable) `direct_io` +* enable (or disable) `auto_cache` +* enable (or disable) `kernel_cache` +* enable (or disable) `splice_move`, `splice_read`, and `splice_write` * increase cache timeouts `cache.attr`, `cache.entry`, `cache.negative_entry` -* enable `cache.open` and/or `cache.statfs` +* enable `cache.open` +* enable `cache.statfs` * enable `cache.symlinks` * change the number opf worker threads * disable `security_capability` and/or `xattr` * disable `posix_acl` +* disable `async_read` * test theoretical performance using `nullrw` or mounting a ram disk * use `symlinkify` if your data is largely static * use tiered cache drives diff --git a/libfuse/include/fuse_common.h b/libfuse/include/fuse_common.h index 3ec08dcb..c233cebb 100644 --- a/libfuse/include/fuse_common.h +++ b/libfuse/include/fuse_common.h @@ -156,11 +156,6 @@ struct fuse_conn_info { */ unsigned proto_minor; - /** - * Is asynchronous read supported (read-write) - */ - unsigned async_read; - /** * Maximum size of the write buffer */ diff --git a/libfuse/lib/fuse_lowlevel.c b/libfuse/lib/fuse_lowlevel.c index 681fc98e..400f2b52 100644 --- a/libfuse/lib/fuse_lowlevel.c +++ b/libfuse/lib/fuse_lowlevel.c @@ -1766,8 +1766,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) } if (arg->minor >= 6) { - if (f->conn.async_read) - f->conn.async_read = arg->flags & FUSE_ASYNC_READ; if (arg->max_readahead < f->conn.max_readahead) f->conn.max_readahead = arg->max_readahead; if (arg->flags & FUSE_ASYNC_READ) @@ -1793,7 +1791,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) if (arg->flags & FUSE_PARALLEL_DIROPS) f->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; } else { - f->conn.async_read = 0; + f->conn.want &= ~FUSE_CAP_ASYNC_READ; f->conn.max_readahead = 0; } @@ -1844,7 +1842,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) if (f->no_splice_move) f->conn.want &= ~FUSE_CAP_SPLICE_MOVE; - if (f->conn.async_read || (f->conn.want & FUSE_CAP_ASYNC_READ)) + if (f->conn.want & FUSE_CAP_ASYNC_READ) outarg.flags |= FUSE_ASYNC_READ; if (f->conn.want & FUSE_CAP_POSIX_LOCKS) outarg.flags |= FUSE_POSIX_LOCKS; @@ -2497,8 +2495,6 @@ static const struct fuse_opt fuse_ll_opts[] = { { "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 }, { "congestion_threshold=%u", offsetof(struct fuse_ll, conn.congestion_threshold), 0 }, - { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 }, - { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 }, { "atomic_o_trunc", offsetof(struct fuse_ll, atomic_o_trunc), 1}, { "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1}, { "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1}, @@ -2532,8 +2528,6 @@ static void fuse_ll_help(void) " -o max_readahead=N set maximum readahead\n" " -o max_background=N set number of maximum background requests\n" " -o congestion_threshold=N set kernel's congestion threshold\n" -" -o async_read perform reads asynchronously (default)\n" -" -o sync_read perform reads synchronously\n" " -o atomic_o_trunc enable atomic open+truncate support\n" " -o big_writes enable larger than 4kB writes\n" " -o no_remote_lock disable remote file locking\n" @@ -2738,7 +2732,6 @@ struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args, goto out; } - f->conn.async_read = 1; f->conn.max_write = UINT_MAX; f->conn.max_readahead = UINT_MAX; f->atomic_o_trunc = 0; diff --git a/man/mergerfs.1 b/man/mergerfs.1 index 669cb995..0b462649 100644 --- a/man/mergerfs.1 +++ b/man/mergerfs.1 @@ -181,6 +181,12 @@ create\[aq]. kernel and underlying filesystem). (default: false) .IP \[bu] 2 +\f[B]async_read=true|false:\f[] Perform reads asynchronously. +If disabled or unavailable the kernel will ensure there is at most one +pending read request per file handle and will attempt to order requests +by offset. +(default: true) +.IP \[bu] 2 \f[B]threads=num\f[]: number of threads to use in multithreaded mode. When set to zero (the default) it will attempt to discover and use the number of logical cores. @@ -1965,19 +1971,21 @@ assuming there are few users. .PP NOTE: be sure to read about these features before changing them .IP \[bu] 2 -add (or remove) \f[C]direct_io\f[] +enable (or disable) \f[C]direct_io\f[] .IP \[bu] 2 -add (or remove) \f[C]auto_cache\f[] +enable (or disable) \f[C]auto_cache\f[] .IP \[bu] 2 -add (or remove) \f[C]kernel_cache\f[] +enable (or disable) \f[C]kernel_cache\f[] .IP \[bu] 2 -add (or remove) \f[C]splice_move\f[], \f[C]splice_read\f[], and +enable (or disable) \f[C]splice_move\f[], \f[C]splice_read\f[], and \f[C]splice_write\f[] .IP \[bu] 2 increase cache timeouts \f[C]cache.attr\f[], \f[C]cache.entry\f[], \f[C]cache.negative_entry\f[] .IP \[bu] 2 -enable \f[C]cache.open\f[] and/or \f[C]cache.statfs\f[] +enable \f[C]cache.open\f[] +.IP \[bu] 2 +enable \f[C]cache.statfs\f[] .IP \[bu] 2 enable \f[C]cache.symlinks\f[] .IP \[bu] 2 @@ -1987,6 +1995,8 @@ disable \f[C]security_capability\f[] and/or \f[C]xattr\f[] .IP \[bu] 2 disable \f[C]posix_acl\f[] .IP \[bu] 2 +disable \f[C]async_read\f[] +.IP \[bu] 2 test theoretical performance using \f[C]nullrw\f[] or mounting a ram disk .IP \[bu] 2 diff --git a/src/config.cpp b/src/config.cpp index 58d0c012..4fb86b56 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -19,12 +19,12 @@ #include "fs.hpp" #include "rwlock.hpp" -#include -#include - #include #include +#include +#include + #define MINFREESPACE_DEFAULT (4294967295ULL) #define POLICYINIT(X) X(policies[FuseFunc::Enum::X]) @@ -50,6 +50,8 @@ Config::Config() statfs_ignore(StatFSIgnore::NONE), posix_acl(false), cache_symlinks(false), + cache_readdir(false), + async_read(true), POLICYINIT(access), POLICYINIT(chmod), POLICYINIT(chown), diff --git a/src/config.hpp b/src/config.hpp index de55b721..1a451541 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -81,6 +81,7 @@ public: bool posix_acl; bool cache_symlinks; bool cache_readdir; + bool async_read; public: const Policy *policies[FuseFunc::Enum::END]; diff --git a/src/fuse_getxattr.cpp b/src/fuse_getxattr.cpp index cdbdd77d..b454138c 100644 --- a/src/fuse_getxattr.cpp +++ b/src/fuse_getxattr.cpp @@ -348,6 +348,8 @@ namespace l l::getxattr_controlfile_bool(config.direct_io,attrvalue); else if(attr[2] == "posix_acl") l::getxattr_controlfile_bool(config.posix_acl,attrvalue); + else if(attr[2] == "async_read") + l::getxattr_controlfile_bool(config.async_read,attrvalue); break; case 4: diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index 09925392..72881cf4 100644 --- a/src/fuse_init.cpp +++ b/src/fuse_init.cpp @@ -34,7 +34,7 @@ namespace l capable(fuse_conn_info *conn_, const int flag_) { - return (conn_->capable & flag_); + return !!(conn_->capable & flag_); } static @@ -45,6 +45,21 @@ namespace l if(capable(conn_,flag_)) want(conn_,flag_); } + + static + void + want_if_capable(fuse_conn_info *conn_, + const int flag_, + bool *want_) + { + if(*want_ && l::capable(conn_,flag_)) + { + l::want(conn_,flag_); + return; + } + + *want_ = false; + } } namespace FUSE @@ -56,23 +71,15 @@ namespace FUSE ugid::init(); - l::want_if_capable(conn_,FUSE_CAP_ASYNC_READ); + l::want_if_capable(conn_,FUSE_CAP_ASYNC_DIO); + l::want_if_capable(conn_,FUSE_CAP_ASYNC_READ,&c.async_read); l::want_if_capable(conn_,FUSE_CAP_ATOMIC_O_TRUNC); l::want_if_capable(conn_,FUSE_CAP_BIG_WRITES); + l::want_if_capable(conn_,FUSE_CAP_CACHE_SYMLINKS,&c.cache_symlinks); l::want_if_capable(conn_,FUSE_CAP_DONT_MASK); l::want_if_capable(conn_,FUSE_CAP_IOCTL_DIR); - l::want_if_capable(conn_,FUSE_CAP_ASYNC_DIO); l::want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS); - - if(c.posix_acl && l::capable(conn_,FUSE_CAP_POSIX_ACL)) - l::want(conn_,FUSE_CAP_POSIX_ACL); - else - c.posix_acl = false; - - if(c.cache_symlinks && l::capable(conn_,FUSE_CAP_CACHE_SYMLINKS)) - l::want(conn_,FUSE_CAP_CACHE_SYMLINKS); - else - c.cache_symlinks = false; + l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&c.posix_acl); return &c; } diff --git a/src/fuse_listxattr.cpp b/src/fuse_listxattr.cpp index 5f3a97c6..70e17bd9 100644 --- a/src/fuse_listxattr.cpp +++ b/src/fuse_listxattr.cpp @@ -44,6 +44,7 @@ namespace l string xattrs; const vector strs = buildvector + ("user.mergerfs.async_read") ("user.mergerfs.branches") ("user.mergerfs.cache.attr") ("user.mergerfs.cache.entry") diff --git a/src/option_parser.cpp b/src/option_parser.cpp index 88739e5b..716be182 100644 --- a/src/option_parser.cpp +++ b/src/option_parser.cpp @@ -99,8 +99,6 @@ static void set_default_options(fuse_args *args) { - set_option(args,"atomic_o_trunc"); - set_option(args,"big_writes"); set_option(args,"default_permissions"); } @@ -256,6 +254,10 @@ parse_and_process_arg(Config &config, return 0; else if(arg == "direct_io") return (config.direct_io=true,0); + else if(arg == "async_read") + return (config.async_read=true,0); + else if(arg == "sync_read") + return (config.async_read=false,0); return 1; } @@ -311,6 +313,10 @@ parse_and_process_kv_arg(Config &config, rv = parse_and_process(value,config.fsname); else if(key == "posix_acl") rv = parse_and_process(value,config.posix_acl); + else if(key == "direct_io") + rv = parse_and_process(value,config.direct_io); + else if(key == "async_read") + rv = parse_and_process(value,config.async_read); } if(rv == -1) @@ -444,7 +450,11 @@ usage(void) " as 'read only' or 'no create'. 'nc' will ignore\n" " available space for branches tagged as\n" " 'no create'. default = none\n" - " -o posix_acl= enable POSIX ACL support\n" + " -o posix_acl= Enable POSIX ACL support. default = false\n" + " -o async_read= If disabled or unavailable the kernel will\n" + " ensure there is at most one pending read \n" + " request per file and will attempt to order\n" + " requests by offset. default = true\n" << std::endl; }