Browse Source

Add flushonclose feature

pull/1275/head
Antonio SJ Musumeci 7 months ago
parent
commit
6aa6452d3e
  1. 26
      README.md
  2. 2
      libfuse/include/fuse_common.h
  3. 2
      libfuse/lib/fuse_lowlevel.c
  4. 25
      man/mergerfs.1
  5. 4
      src/config.cpp
  6. 4
      src/config.hpp
  7. 54
      src/config_flushonclose.cpp
  8. 31
      src/config_flushonclose.hpp
  9. 28
      src/fuse_create.cpp
  10. 21
      src/fuse_open.cpp
  11. 2
      src/fuse_opendir.cpp

26
README.md

@ -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

2
libfuse/include/fuse_common.h

@ -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;

2
libfuse/lib/fuse_lowlevel.c

@ -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

25
man/mergerfs.1

@ -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

4
src/config.cpp

@ -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;

4
src/config.hpp

@ -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;

54
src/config_flushonclose.cpp

@ -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;
}

31
src/config_flushonclose.hpp

@ -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;

28
src/fuse_create.cpp

@ -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,

21
src/fuse_open.cpp

@ -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_,

2
src/fuse_opendir.cpp

@ -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;

Loading…
Cancel
Save