mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-25 18:40:18 -04:00
Add ability to proxy ioprio of client apps (#1534)
This commit is contained in:
parent
2f3e807e9d
commit
569a151da2
13 changed files with 217 additions and 1 deletions
|
|
@ -29,3 +29,15 @@ of the `readdir` call. In v6.16 that was changed to be able to grow to
|
|||
the [fuse_msg_size](fuse_msg_size.md) which allows a lot more data to
|
||||
be sent per request and therefore much better performance in certain
|
||||
situations.
|
||||
|
||||
|
||||
## Technical Details
|
||||
|
||||
* On Linux
|
||||
[getdents](https://man7.org/linux/man-pages/man2/getdents.2.html) is
|
||||
used instead of
|
||||
[readdir](https://man7.org/linux/man-pages/man2/readdir.2.html) in
|
||||
order to leverage larger buffers for better performance on larger
|
||||
directory reads as well as have more control in concurrent policies.
|
||||
* [readdir](https://man7.org/linux/man-pages/man2/readdir.2.html) is
|
||||
used on FreeBSD.
|
||||
|
|
|
|||
|
|
@ -162,6 +162,8 @@ config file.
|
|||
* **scheduling-priority=INT**: Set mergerfs' scheduling
|
||||
priority. Valid values range from -20 to 19. See `setpriority` man
|
||||
page for more details. (default: -10)
|
||||
* **[proxy-ioprio](proxy-ioprio.md)=BOOL**: Use ioprio value from
|
||||
calling threads when reading and writing. (default: false)
|
||||
* **fsname=STR**: 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.
|
||||
|
|
|
|||
53
mkdocs/docs/config/proxy-ioprio.md
Normal file
53
mkdocs/docs/config/proxy-ioprio.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# proxy-ioprio
|
||||
|
||||
* type: `BOOL`
|
||||
* default: `false`
|
||||
* example: `proxy-ioprio=true`
|
||||
|
||||
In Linux certain process schedulers have the ability to
|
||||
[prioritize](https://man7.org/linux/man-pages/man2/ioprio_set.2.html)
|
||||
based on a per thread [IO
|
||||
priority](https://www.kernel.org/doc/Documentation/block/ioprio.txt)
|
||||
assigned to the software by users using tools such as
|
||||
[ionice](https://man7.org/linux/man-pages/man1/ionice.1.html). Since
|
||||
such details are not provided by the FUSE protocol, users may not use
|
||||
schedulers which support IO priority, and the extra overhead to query
|
||||
the priority and set it within mergerfs it does not by default attempt
|
||||
to mirror/proxy the value. When enabled however, for all read and
|
||||
write requests, mergerfs will query the priority of the requesting
|
||||
process/thread and set the same value on the thread within mergerfs
|
||||
making the read/write.
|
||||
|
||||
This may be useful in situation where the system has high IO load or
|
||||
processes are known to have varying priorities. See the [man
|
||||
page](https://man7.org/linux/man-pages/man2/ioprio_set.2.html) for
|
||||
specific details.
|
||||
|
||||
Keep in mind that if no IO scheduler has been set for a thread then by
|
||||
default the IO priority will match the CPU nice value which for
|
||||
mergerfs is set by [scheduling-priority](options.md). [Nice
|
||||
value](https://man7.org/linux/man-pages/man2/getpriority.2.html)
|
||||
proxying may be added in a future release.
|
||||
|
||||
|
||||
## Conflicts With Other Options
|
||||
|
||||
If using [IO passthrough](passthrough.md) there is no reason to set
|
||||
this value to `true`. However, since `passthrough` leads to mergerfs
|
||||
IO calls being bypassed entirely will have no impact on performance or
|
||||
behavior regardless.
|
||||
|
||||
When using `passthrough` the IO priority of the client app would be
|
||||
used directly given the IO is passed through.
|
||||
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
Only Linux. This is not supported on FreeBSD.
|
||||
|
||||
|
||||
## Performance Impact
|
||||
|
||||
Despite the additional syscalls requires the impact appears
|
||||
undetectable with larger buffer sizes. At very small buffer sizes
|
||||
(such as 512 bytes) there is a noticable but small impact (~1.5%).
|
||||
|
|
@ -81,6 +81,7 @@ nav:
|
|||
- config/inodecalc.md
|
||||
- config/threads.md
|
||||
- config/pin-threads.md
|
||||
- config/proxy-ioprio.md
|
||||
- config/link_cow.md
|
||||
- config/fuse_msg_size.md
|
||||
- config/follow-symlinks.md
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ Config::Config()
|
|||
nullrw(false),
|
||||
parallel_direct_writes(true),
|
||||
posix_acl(false),
|
||||
proxy_ioprio(false),
|
||||
readahead(0),
|
||||
readdir("seq"),
|
||||
readdirplus(false),
|
||||
|
|
@ -207,6 +208,7 @@ Config::Config()
|
|||
_map["parallel-direct-writes"] = ¶llel_direct_writes;
|
||||
_map["pin-threads"] = &fuse_pin_threads;
|
||||
_map["posix_acl"] = &posix_acl;
|
||||
_map["proxy-ioprio"] = &proxy_ioprio;
|
||||
_map["readahead"] = &readahead;
|
||||
_map["readdirplus"] = &readdirplus;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "config_pagesize.hpp"
|
||||
#include "config_passthrough.hpp"
|
||||
#include "config_pid.hpp"
|
||||
#include "config_proxy_ioprio.hpp"
|
||||
#include "config_rename_exdev.hpp"
|
||||
#include "config_set.hpp"
|
||||
#include "config_statfs.hpp"
|
||||
|
|
@ -126,6 +127,7 @@ public:
|
|||
ConfigBOOL parallel_direct_writes;
|
||||
ConfigGetPid pid;
|
||||
ConfigBOOL posix_acl;
|
||||
ProxyIOPrio proxy_ioprio;
|
||||
ConfigUINT64 readahead;
|
||||
FUSE::ReadDir readdir;
|
||||
ConfigBOOL readdirplus;
|
||||
|
|
|
|||
33
src/config_proxy_ioprio.cpp
Normal file
33
src/config_proxy_ioprio.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include "config_proxy_ioprio.hpp"
|
||||
|
||||
#include "ioprio.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
|
||||
ProxyIOPrio::ProxyIOPrio(const bool b_)
|
||||
{
|
||||
ioprio::enable(b_);
|
||||
}
|
||||
|
||||
std::string
|
||||
ProxyIOPrio::to_string(void) const
|
||||
{
|
||||
if(ioprio::enabled())
|
||||
return "true";
|
||||
return "false";
|
||||
}
|
||||
|
||||
int
|
||||
ProxyIOPrio::from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
bool enable;
|
||||
|
||||
rv = str::from(s_,&enable);
|
||||
if(rv)
|
||||
return rv;
|
||||
|
||||
ioprio::enable(enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
13
src/config_proxy_ioprio.hpp
Normal file
13
src/config_proxy_ioprio.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
class ProxyIOPrio : public ToFromString
|
||||
{
|
||||
public:
|
||||
ProxyIOPrio(const bool);
|
||||
|
||||
public:
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string_view) final;
|
||||
};
|
||||
|
|
@ -24,7 +24,7 @@ public:
|
|||
operator=(int v_)
|
||||
{
|
||||
if(!_err.has_value())
|
||||
_err = v_;
|
||||
_err = ((v_ >= 0) ? 0 : v_);
|
||||
else if(v_ >= 0)
|
||||
_err = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "errno.hpp"
|
||||
#include "fileinfo.hpp"
|
||||
#include "fs_pread.hpp"
|
||||
#include "ioprio.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
|
@ -60,6 +61,7 @@ FUSE::read(const fuse_file_info_t *ffi_,
|
|||
size_t size_,
|
||||
off_t offset_)
|
||||
{
|
||||
ioprio::SetFrom iop(fuse_get_context()->pid);
|
||||
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
|
||||
|
||||
if(fi->direct_io)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "fs_movefile_and_open.hpp"
|
||||
#include "fs_pwrite.hpp"
|
||||
#include "fs_pwriten.hpp"
|
||||
#include "ioprio.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
|
@ -187,6 +188,8 @@ FUSE::write(const fuse_file_info_t *ffi_,
|
|||
size_t count_,
|
||||
off_t offset_)
|
||||
{
|
||||
ioprio::SetFrom iop(fuse_get_context()->pid);
|
||||
|
||||
return ::_write(ffi_,buf_,count_,offset_);
|
||||
}
|
||||
|
||||
|
|
|
|||
73
src/ioprio.cpp
Normal file
73
src/ioprio.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#include "ioprio.hpp"
|
||||
|
||||
#ifdef __linux__
|
||||
# include <linux/ioprio.h>
|
||||
# include <sys/syscall.h>
|
||||
# include <unistd.h>
|
||||
# include <errno.h>
|
||||
#else
|
||||
#warning "ioprio not supported on this platform"
|
||||
#endif
|
||||
|
||||
thread_local int ioprio::SetFrom::thread_prio = -1;
|
||||
bool _enabled = false;
|
||||
|
||||
int
|
||||
ioprio::get(const int which_,
|
||||
const int who_)
|
||||
{
|
||||
#ifdef SYS_ioprio_get
|
||||
int rv;
|
||||
|
||||
rv = syscall(SYS_ioprio_get,which_,who_);
|
||||
|
||||
return ((rv == -1) ? -errno : rv);
|
||||
#else
|
||||
return -ENOSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
ioprio::set(const int which_,
|
||||
const int who_,
|
||||
const int ioprio_)
|
||||
{
|
||||
#ifdef SYS_ioprio_set
|
||||
int rv;
|
||||
|
||||
rv = syscall(SYS_ioprio_set,which_,who_,ioprio_);
|
||||
|
||||
return ((rv == -1) ? -errno : rv);
|
||||
#else
|
||||
return -ENOSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ioprio::enable(const bool enable_)
|
||||
{
|
||||
_enabled = enable_;
|
||||
}
|
||||
|
||||
bool
|
||||
ioprio::enabled()
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
ioprio::SetFrom::SetFrom(const pid_t pid_)
|
||||
{
|
||||
int client_prio;
|
||||
|
||||
if(!_enabled)
|
||||
return;
|
||||
|
||||
client_prio = ioprio::get(IOPRIO_WHO_PROCESS,pid_);
|
||||
if(client_prio < 0)
|
||||
return;
|
||||
if(client_prio == thread_prio)
|
||||
return;
|
||||
|
||||
thread_prio = client_prio;
|
||||
ioprio::set(IOPRIO_WHO_PROCESS,0,client_prio);
|
||||
}
|
||||
20
src/ioprio.hpp
Normal file
20
src/ioprio.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace ioprio
|
||||
{
|
||||
void enable(const bool);
|
||||
bool enabled();
|
||||
|
||||
int get(const int which, const int who);
|
||||
int set(const int which, const int who, const int ioprio);
|
||||
|
||||
struct SetFrom
|
||||
{
|
||||
static thread_local int thread_prio;
|
||||
|
||||
SetFrom(const pid_t pid);
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue