From 486c5d3734f00124f134091f3755670a539eae8f Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Tue, 28 Feb 2023 23:10:36 -0500 Subject: [PATCH] Add ability to set readahead of mergerfs and branches --- README.md | 5 +-- man/mergerfs.1 | 10 +++--- src/config.cpp | 2 ++ src/config.hpp | 1 + src/fs_readahead.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++ src/fs_readahead.hpp | 39 ++++++++++++++++++++ src/fuse_init.cpp | 51 ++++++++++++++++++++++++++ src/mergerfs.cpp | 1 + 8 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 src/fs_readahead.cpp create mode 100644 src/fs_readahead.hpp diff --git a/README.md b/README.md index c5b6da3e..28f87d8f 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,8 @@ These options are the same regardless of whether you use them with the * **rename-exdev=passthrough|rel-symlink|abs-symlink**: When a rename fails with EXDEV optionally move the file to a special directory and symlink to it. +* **readahead=UINT**: Set readahead (in kilobytes) for mergerfs and + branches if greater than 0. (default: 0) * **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false) * **async_read=BOOL**: Perform reads asynchronously. If disabled or @@ -1528,8 +1530,7 @@ understand what behaviors it may impact * use `symlinkify` if your data is largely static and read-only * use tiered cache drives * use LVM and LVM cache to place a SSD in front of your HDDs -* if `cache.files` is enabled increase readahead: `echo "1024" > /sys/class/bdi/0:$(stat -c%d /MOUNT)/read_ahead_kb` -* increase readahead on all devices: `ls -1 /sys/class/bdi/*/read_ahead_kb | xargs -n1 -I{} sh -c "echo 1024 > {}"` +* increase readahead: `readahead=1024` If you come across a setting that significantly impacts performance please contact trapexit so he may investigate further. Please test diff --git a/man/mergerfs.1 b/man/mergerfs.1 index 2b6d8768..89356b75 100644 --- a/man/mergerfs.1 +++ b/man/mergerfs.1 @@ -265,6 +265,10 @@ instead. rename fails with EXDEV optionally move the file to a special directory and symlink to it. .IP \[bu] 2 +\f[B]readahead=UINT\f[R]: Set readahead (in kilobytes) for mergerfs and +branches if greater than 0. +(default: 0) +.IP \[bu] 2 \f[B]posix_acl=BOOL\f[R]: Enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false) @@ -1982,11 +1986,7 @@ use tiered cache drives .IP \[bu] 2 use LVM and LVM cache to place a SSD in front of your HDDs .IP \[bu] 2 -if \f[C]cache.files\f[R] is enabled increase readahead: -\f[C]echo \[dq]1024\[dq] > /sys/class/bdi/0:$(stat -c%d /MOUNT)/read_ahead_kb\f[R] -.IP \[bu] 2 -increase readahead on all devices: -\f[C]ls -1 /sys/class/bdi/*/read_ahead_kb | xargs -n1 -I{} sh -c \[dq]echo 1024 > {}\[dq]\f[R] +increase readahead: \f[C]readahead=1024\f[R] .PP If you come across a setting that significantly impacts performance please contact trapexit so he may investigate further. diff --git a/src/config.cpp b/src/config.cpp index 59aea678..2f7c495b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -105,6 +105,7 @@ Config::Config() nullrw(false), pid(::getpid()), posix_acl(false), + readahead(0), readdir(ReadDir::ENUM::POSIX), readdirplus(false), rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH), @@ -176,6 +177,7 @@ Config::Config() _map["nullrw"] = &nullrw; _map["pid"] = &pid; _map["posix_acl"] = &posix_acl; + _map["readahead"] = &readahead; // _map["readdir"] = &readdir; _map["readdirplus"] = &readdirplus; _map["rename-exdev"] = &rename_exdev; diff --git a/src/config.hpp b/src/config.hpp index ff41cb42..40b98d0a 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -133,6 +133,7 @@ public: ConfigBOOL nullrw; ConfigUINT64 pid; ConfigBOOL posix_acl; + ConfigUINT64 readahead; ReadDir readdir; ConfigBOOL readdirplus; RenameEXDEV rename_exdev; diff --git a/src/fs_readahead.cpp b/src/fs_readahead.cpp new file mode 100644 index 00000000..740c28a7 --- /dev/null +++ b/src/fs_readahead.cpp @@ -0,0 +1,86 @@ +/* + ISC License + + Copyright (c) 2023, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "fs_readahead.hpp" + +#include "fmt/core.h" +#include "fs_lstat.hpp" + +#include +#include +#include + +#include + +namespace l +{ + static + std::string + generate_readahead_sys_path(const std::uint64_t major_, + const std::uint64_t minor_) + { + return fmt::format("/sys/class/bdi/{}:{}/read_ahead_kb",major_,minor_); + } +} + +int +fs::readahead(const std::uint64_t major_dev_, + const std::uint64_t minor_dev_, + const std::uint64_t size_in_kb_) +{ + std::string syspath; + std::ofstream ofs; + + syspath = l::generate_readahead_sys_path(major_dev_,minor_dev_); + + ofs.open(syspath); + if(ofs) + { + ofs << fmt::format("{}\n",size_in_kb_); + ofs.close(); + } + + return 0; +} + +int +fs::readahead(const std::uint64_t dev_, + const std::uint64_t size_in_kb_) +{ + std::uint32_t major_dev; + std::uint32_t minor_dev; + + major_dev = major(dev_); + minor_dev = minor(dev_); + + return fs::readahead(major_dev,minor_dev,size_in_kb_); +} + +int +fs::readahead(const std::string path_, + const std::uint64_t size_in_kb_) +{ + int rv; + struct stat st; + + rv = fs::lstat(path_,&st); + if(rv == -1) + return -errno; + + return fs::readahead(st.st_dev,size_in_kb_); +} diff --git a/src/fs_readahead.hpp b/src/fs_readahead.hpp new file mode 100644 index 00000000..37963292 --- /dev/null +++ b/src/fs_readahead.hpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2023, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +#include +#include + + +namespace fs +{ + int + readahead(const std::uint64_t major_dev, + const std::uint64_t minor_dev, + const std::uint64_t size_in_kb); + + int + readahead(const std::uint64_t dev, + const std::uint64_t size_in_kb); + + int + readahead(const std::string path, + const std::uint64_t size_in_kb); +} diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index e46c727b..4fc7864e 100644 --- a/src/fuse_init.cpp +++ b/src/fuse_init.cpp @@ -16,9 +16,15 @@ #include "config.hpp" #include "ugid.hpp" +#include "fs_readahead.hpp" +#include "syslog.hpp" + +#include "fmt/core.h" #include "fuse.h" +#include + namespace l { @@ -77,6 +83,49 @@ namespace l cfg_->fuse_msg_size = FUSE_DEFAULT_MAX_PAGES_PER_REQ; } } + + static + void + readahead(const std::string path_, + const int readahead_) + { + int rv; + + rv = fs::readahead(path_,readahead_); + if(rv == 0) + syslog_info("%s - readahead set to %d",path_.c_str(),readahead_); + else + syslog_error("%s - unable to set readahead",path_.c_str()); + } + + static + void + set_readahead_on_mount_and_branches() + { + Config::Read cfg; + Branches::CPtr branches; + + if((uint64_t)cfg->readahead == 0) + return; + + l::readahead(cfg->mountpoint,cfg->readahead); + + branches = cfg->branches; + for(auto const &branch : *branches) + l::readahead(branch.path,cfg->readahead); + } + + // Spawn a thread to do this because before init returns calls to + // set the value will block leading to a deadlock. This is just + // easier. + static + void + spawn_thread_to_set_readahead() + { + std::thread readahead_thread(l::set_readahead_on_mount_and_branches); + + readahead_thread.detach(); + } } namespace FUSE @@ -104,6 +153,8 @@ namespace FUSE conn_->want &= ~FUSE_CAP_POSIX_LOCKS; conn_->want &= ~FUSE_CAP_FLOCK_LOCKS; + l::spawn_thread_to_set_readahead(); + return NULL; } } diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index 248f7c51..96fb82c8 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -14,6 +14,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fs_readahead.hpp" #include "fs_wait_for_mount.hpp" #include "syslog.hpp"