Add flushonclose feature

This commit is contained in:
Antonio SJ Musumeci 2023-10-23 22:32:42 -05:00
parent 7d6c8e9333
commit 6aa6452d3e
11 changed files with 196 additions and 3 deletions

View File

@ -248,7 +248,10 @@ These options are the same regardless of whether you use them with the
to the same as the process thread count. (default: 0)
* **pin-threads=STR**: Selects a strategy to pin threads to CPUs
(default: unset)
* **scheduling-priority=INT**: Set mergerfs' scheduling
* **flush-on-close=never|always|opened-for-write**: Flush data cache
on file close. Mostly for when writeback is enabled or merging
network filesystems. (default: opened-for-write)
* **scheduling-priority=INT**: Set mergerfs' scheduling
priority. Valid values range from -20 to 19. See `setpriority` man
page for more details. (default: -10)
* **fsname=STR**: Sets the name of the filesystem as seen in
@ -926,6 +929,27 @@ The options `statfs` and `statfs_ignore` can be used to modify
`statfs` behavior.
#### flush-on-close
https://lkml.kernel.org/linux-fsdevel/20211024132607.1636952-1-amir73il@gmail.com/T/
By default FUSE would issue a flush before the release of a file
descriptor. This was considered a bit aggressive and a feature added
to give the FUSE server the ability to choose when that happens.
Options:
* always
* never
* opened-for-write
For now it defaults to "opened-for-write" which is less aggressive
than the behavior before this feature was added. It should not be a
problem because the flush is really only relevant when a file is
written to. Given flush is irrelevant for many filesystems in the
future a branch specific flag may be added so only files opened on a
specific branch would be flushed on close.
# ERROR HANDLING
POSIX filesystem functions offer a single return code meaning that

View File

@ -86,6 +86,8 @@ struct fuse_file_info_t
uint32_t parallel_direct_writes:1;
uint32_t noflush:1;
/** File handle. May be filled in by filesystem in open().
Available in all other file operations */
uint64_t fh;

View File

@ -275,6 +275,8 @@ fill_open(struct fuse_open_out *arg_,
arg_->open_flags |= FOPEN_CACHE_DIR;
if(ffi_->parallel_direct_writes)
arg_->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
if(ffi_->noflush)
arg_->open_flags |= FOPEN_NOFLUSH;
}
int

View File

@ -321,11 +321,18 @@ by the number of process threads plus read thread count.
\f[B]pin-threads=STR\f[R]: Selects a strategy to pin threads to CPUs
(default: unset)
.IP \[bu] 2
\f[B]flush-on-close=never|always|opened-for-write\f[R]: Flush data cache
on file close.
Mostly for when writeback is enabled or merging network filesystems.
(default: opened-for-write)
.RS 2
.IP \[bu] 2
\f[B]scheduling-priority=INT\f[R]: Set mergerfs\[cq] scheduling
priority.
Valid values range from -20 to 19.
See \f[C]setpriority\f[R] man page for more details.
(default: -10)
.RE
.IP \[bu] 2
\f[B]fsname=STR\f[R]: Sets the name of the filesystem as seen in
\f[B]mount\f[R], \f[B]df\f[R], etc.
@ -1321,6 +1328,24 @@ be included when checking the mount\[cq]s stats.
.PP
The options \f[C]statfs\f[R] and \f[C]statfs_ignore\f[R] can be used to
modify \f[C]statfs\f[R] behavior.
.SS flush-on-close
.PP
https://lkml.kernel.org/linux-fsdevel/20211024132607.1636952-1-amir73il\[at]gmail.com/T/
.PP
By default FUSE would issue a flush before the release of a file
descriptor.
This was considered a bit aggressive and a feature added to give the
FUSE server the ability to choose when that happens.
.PP
Options: * always * never * opened-for-write
.PP
For now it defaults to \[lq]opened-for-write\[rq] which is less
aggressive than the behavior before this feature was added.
It should not be a problem because the flush is really only relevant
when a file is written to.
Given flush is irrelevant for many filesystems in the future a branch
specific flag may be added so only files opened on a specific branch
would be flushed on close.
.SH ERROR HANDLING
.PP
POSIX filesystem functions offer a single return code meaning that there

View File

@ -94,8 +94,9 @@ Config::Config()
category(func),
direct_io(false),
dropcacheonclose(false),
fsname(),
flushonclose(FlushOnClose::ENUM::ALWAYS),
follow_symlinks(FollowSymlinks::ENUM::NEVER),
fsname(),
func(),
fuse_msg_size(FUSE_MAX_MAX_PAGES),
ignorepponrename(false),
@ -149,6 +150,7 @@ Config::Config()
_map["category.search"] = &category.search;
_map["direct_io"] = &direct_io;
_map["dropcacheonclose"] = &dropcacheonclose;
_map["flush-on-close"] = &flushonclose;
_map["follow-symlinks"] = &follow_symlinks;
_map["fsname"] = &fsname;
_map["func.access"] = &func.access;

View File

@ -19,6 +19,7 @@
#include "branches.hpp"
#include "category.hpp"
#include "config_cachefiles.hpp"
#include "config_flushonclose.hpp"
#include "config_follow_symlinks.hpp"
#include "config_inodecalc.hpp"
#include "config_link_exdev.hpp"
@ -116,8 +117,9 @@ public:
Categories category;
ConfigBOOL direct_io;
ConfigBOOL dropcacheonclose;
ConfigSTR fsname;
FlushOnClose flushonclose;
FollowSymlinks follow_symlinks;
ConfigSTR fsname;
Funcs func;
ConfigUINT64 fuse_msg_size;
ConfigBOOL ignorepponrename;

View File

@ -0,0 +1,54 @@
/*
ISC License
Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
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 "config_flushonclose.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
FlushOnClose::to_string() const
{
switch(_data)
{
case FlushOnClose::ENUM::NEVER:
return "never";
case FlushOnClose::ENUM::OPENED_FOR_WRITE:
return "opened-for-write";
case FlushOnClose::ENUM::ALWAYS:
return "always";
}
return {};
}
template<>
int
FlushOnClose::from_string(const std::string &s_)
{
if(s_ == "never")
_data = FlushOnClose::ENUM::NEVER;
ef(s_ == "opened-for-write")
_data = FlushOnClose::ENUM::OPENED_FOR_WRITE;
ef(s_ == "always")
_data = FlushOnClose::ENUM::ALWAYS;
else
return -EINVAL;
return 0;
}

View File

@ -0,0 +1,31 @@
/*
ISC License
Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
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 "enum.hpp"
enum class FlushOnCloseEnum
{
NEVER,
OPENED_FOR_WRITE,
ALWAYS
};
typedef Enum<FlushOnCloseEnum> FlushOnClose;

View File

@ -51,6 +51,31 @@ namespace l
*flags_ &= ~O_APPEND;
}
static
bool
rdonly(const int flags_)
{
return ((flags_ & O_ACCMODE) == O_RDONLY);
}
static
bool
calculate_flush(FlushOnClose const flushonclose_,
int const flags_)
{
switch(flushonclose_)
{
case FlushOnCloseEnum::NEVER:
return false;
case FlushOnCloseEnum::OPENED_FOR_WRITE:
return !l::rdonly(flags_);
case FlushOnCloseEnum::ALWAYS:
return true;
}
return true;
}
static
void
config_to_ffi_flags(Config::Read &cfg_,
@ -200,6 +225,9 @@ namespace FUSE
if(cfg->writeback_cache)
l::tweak_flags_writeback_cache(&ffi_->flags);
ffi_->noflush = !l::calculate_flush(cfg->flushonclose,
ffi_->flags);
rv = l::create(cfg->func.getattr.policy,
cfg->func.create.policy,
cfg->branches,

View File

@ -114,6 +114,24 @@ namespace l
*flags_ &= ~O_APPEND;
}
static
bool
calculate_flush(FlushOnClose const flushonclose_,
int const flags_)
{
switch(flushonclose_)
{
case FlushOnCloseEnum::NEVER:
return false;
case FlushOnCloseEnum::OPENED_FOR_WRITE:
return !l::rdonly(flags_);
case FlushOnCloseEnum::ALWAYS:
return true;
}
return true;
}
static
void
config_to_ffi_flags(Config::Read &cfg_,
@ -236,6 +254,9 @@ namespace FUSE
if(cfg->writeback_cache)
l::tweak_flags_writeback_cache(&ffi_->flags);
ffi_->noflush = !l::calculate_flush(cfg->flushonclose,
ffi_->flags);
rv = l::open(cfg->func.open.policy,
cfg->branches,
fusepath_,

View File

@ -30,6 +30,8 @@ namespace FUSE
ffi_->fh = reinterpret_cast<uint64_t>(new DirInfo(fusepath_));
ffi_->noflush = true;
if(cfg->cache_readdir)
{
ffi_->keep_cache = 1;