Browse Source

Add support for file io passthrough

If using Linux 6.9 or above and enabled (passthrough=true) files
opened or created will use the FUSE passthrough feature.

If direct-io=true / cache.files=off it will override passthrough. If
direct-io-allow-mmap is enabled only mmap will passthrough.

HANDLE_KILLPRIV and V2 are enabled now by default to remove the
kernel's need to issue getattr and getxattr requests.

moveonenospc will not work when leveraging passthrough.
passthrough
Antonio SJ Musumeci 8 months ago
parent
commit
c9b7ed5b8a
  1. 7
      libfuse/include/fuse.h
  2. 19
      libfuse/include/fuse_common.h
  3. 39
      libfuse/include/fuse_kernel.h
  4. 1
      libfuse/lib/debug.c
  5. 34
      libfuse/lib/fuse.c
  6. 20
      libfuse/lib/fuse_lowlevel.c
  7. 8
      src/config.cpp
  8. 3
      src/config.hpp
  9. 2
      src/fileinfo.hpp
  10. 27
      src/fuse_create.cpp
  11. 3
      src/fuse_init.cpp
  12. 27
      src/fuse_open.cpp
  13. 9
      src/fuse_release.cpp

7
libfuse/include/fuse.h

@ -667,6 +667,8 @@ int fuse_loop_mt(struct fuse *f);
*/
struct fuse_context *fuse_get_context(void);
int fuse_get_dev_fuse_fd(const struct fuse_context *);
/**
* Check if the current request has already been interrupted
*
@ -779,6 +781,11 @@ void fuse_gc1();
void fuse_gc();
void fuse_invalidate_all_nodes();
int fuse_passthrough_open(const struct fuse_context *fc,
const int fd);
int fuse_passthrough_close(const struct fuse_context *fc,
const int backing_id);
EXTERN_C_END
#endif /* _FUSE_H_ */

19
libfuse/include/fuse_common.h

@ -88,26 +88,18 @@ struct fuse_file_info_t
uint32_t noflush:1;
uint32_t passthrough:1;
/** File handle. May be filled in by filesystem in open().
Available in all other file operations */
uint64_t fh;
uint32_t backing_id;
/** Lock owner id. Available in locking operations and flush */
uint64_t lock_owner;
};
/**
* Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
*
* FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
* FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
* FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
* FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
* FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
* FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
* FUSE_CAP_IOCTL_DIR: ioctl support on directories
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
*/
#define FUSE_CAP_ASYNC_READ (1ULL << 0)
#define FUSE_CAP_POSIX_LOCKS (1ULL << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1ULL << 3)
@ -127,6 +119,9 @@ struct fuse_file_info_t
#define FUSE_CAP_SETXATTR_EXT (1ULL << 22)
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1ULL << 23)
#define FUSE_CAP_CREATE_SUPP_GROUP (1ULL << 24)
#define FUSE_CAP_PASSTHROUGH (1ULL << 25)
#define FUSE_CAP_HANDLE_KILLPRIV (1ULL << 26)
#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1ULL << 27)
/**

39
libfuse/include/fuse_kernel.h

@ -211,6 +211,12 @@
* 7.39
* - add FUSE_DIRECT_IO_ALLOW_MMAP
* - add FUSE_STATX and related structures
*
* 7.40
* - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag
* - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
* - add FUSE_NO_EXPORT_SUPPORT init flag
* - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
*/
#ifndef _LINUX_FUSE_H
@ -246,7 +252,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 39
#define FUSE_KERNEL_MINOR_VERSION 40
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@ -353,6 +359,7 @@ struct fuse_file_lock {
* FOPEN_STREAM: the file is stream-like (no file position at all)
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
* FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode
* FOPEN_PASSTHROUGH: passthrough read/write io for this open file
*/
#define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1)
@ -361,6 +368,7 @@ struct fuse_file_lock {
#define FOPEN_STREAM (1 << 4)
#define FOPEN_NOFLUSH (1 << 5)
#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
#define FOPEN_PASSTHROUGH (1 << 7)
/**
* INIT request/reply flags
@ -410,6 +418,9 @@ struct fuse_file_lock {
* symlink and mknod (single group that matches parent)
* FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation
* FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode.
* FUSE_NO_EXPORT_SUPPORT: explicitly disable export support
* FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit
* of the request ID indicates resend requests
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@ -449,6 +460,9 @@ struct fuse_file_lock {
#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36)
#define FUSE_PASSTHROUGH (1ULL << 37)
#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
#define FUSE_HAS_RESEND (1ULL << 39)
/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
@ -635,6 +649,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_STORE = 4,
FUSE_NOTIFY_RETRIEVE = 5,
FUSE_NOTIFY_DELETE = 6,
FUSE_NOTIFY_RESEND = 7,
FUSE_NOTIFY_CODE_MAX,
};
@ -761,7 +776,7 @@ struct fuse_create_in {
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
uint32_t padding;
int32_t backing_id;
};
struct fuse_release_in {
@ -877,7 +892,8 @@ struct fuse_init_out {
uint16_t max_pages;
uint16_t map_alignment;
uint32_t flags2;
uint32_t unused[7];
uint32_t max_stack_depth;
uint32_t unused[6];
};
#define CUSE_INIT_INFO_MAX 4096
@ -960,6 +976,14 @@ struct fuse_fallocate_in {
uint32_t padding;
};
/**
* FUSE request unique ID flag
*
* Indicates whether this is a resend request. The receiver should handle this
* request accordingly.
*/
#define FUSE_UNIQUE_RESEND (1ULL << 63)
struct fuse_in_header {
uint32_t len;
uint32_t opcode;
@ -1049,9 +1073,18 @@ struct fuse_notify_retrieve_in {
uint64_t dummy4;
};
struct fuse_backing_map {
int32_t fd;
uint32_t flags;
uint64_t padding;
};
/* Device ioctls: */
#define FUSE_DEV_IOC_MAGIC 229
#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \
struct fuse_backing_map)
#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
struct fuse_lseek_in {
uint64_t fh;

1
libfuse/lib/debug.c

@ -199,6 +199,7 @@ fuse_flag_to_str(const uint64_t offset_)
FUSE_INIT_FLAG_CASE(CREATE_SUPP_GROUP);
FUSE_INIT_FLAG_CASE(HAS_EXPIRE_ONLY);
FUSE_INIT_FLAG_CASE(DIRECT_IO_ALLOW_MMAP);
FUSE_INIT_FLAG_CASE(PASSTHROUGH);
}
return NULL;

34
libfuse/lib/fuse.c

@ -47,6 +47,7 @@
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <sys/ioctl.h>
#ifdef HAVE_MALLOC_TRIM
#include <malloc.h>
@ -3682,6 +3683,12 @@ fuse_get_context(void)
return &fuse_get_context_internal()->ctx;
}
int
fuse_get_dev_fuse_fd(const struct fuse_context *fc_)
{
return fuse_chan_fd(fc_->fuse->se->ch);
}
enum {
KEY_HELP,
};
@ -4156,3 +4163,30 @@ fuse_log_metrics_get(void)
{
return g_LOG_METRICS;
}
int
fuse_passthrough_open(const struct fuse_context *fc_,
const int fd_)
{
int rv;
int dev_fuse_fd;
struct fuse_backing_map bm = {0};
dev_fuse_fd = fuse_get_dev_fuse_fd(fc_);
bm.fd = fd_;
rv = ioctl(dev_fuse_fd,FUSE_DEV_IOC_BACKING_OPEN,&bm);
return rv;
}
int
fuse_passthrough_close(const struct fuse_context *fc_,
const int backing_id_)
{
int dev_fuse_fd;
dev_fuse_fd = fuse_get_dev_fuse_fd(fc_);
return ioctl(dev_fuse_fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id_);
}

20
libfuse/lib/fuse_lowlevel.c

@ -277,6 +277,11 @@ fill_open(struct fuse_open_out *arg_,
arg_->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
if(ffi_->noflush)
arg_->open_flags |= FOPEN_NOFLUSH;
if(ffi_->passthrough)
{
arg_->open_flags |= FOPEN_PASSTHROUGH;
arg_->backing_id = ffi_->backing_id;
}
}
int
@ -1175,6 +1180,12 @@ do_init(fuse_req_t req,
f->conn.capable |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
if(inargflags & FUSE_CREATE_SUPP_GROUP)
f->conn.capable |= FUSE_CAP_CREATE_SUPP_GROUP;
if(inargflags & FUSE_PASSTHROUGH)
f->conn.capable |= FUSE_CAP_PASSTHROUGH;
if(inargflags & FUSE_HANDLE_KILLPRIV)
f->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
if(inargflags & FUSE_HANDLE_KILLPRIV_V2)
f->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV_V2;
}
else
{
@ -1248,6 +1259,15 @@ do_init(fuse_req_t req,
outargflags |= FUSE_CREATE_SUPP_GROUP;
if(f->conn.want & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
if(f->conn.want & FUSE_CAP_HANDLE_KILLPRIV)
outargflags |= FUSE_HANDLE_KILLPRIV;
if(f->conn.want & FUSE_CAP_HANDLE_KILLPRIV_V2)
outargflags |= FUSE_HANDLE_KILLPRIV_V2;
if(f->conn.want & FUSE_CAP_PASSTHROUGH)
{
outargflags |= FUSE_PASSTHROUGH;
outarg.max_stack_depth = 2;
}
if(inargflags & FUSE_INIT_EXT)
{

8
src/config.cpp

@ -62,6 +62,8 @@ namespace l
IFERT("export-support");
IFERT("fsname");
IFERT("fuse_msg_size");
IFERT("handle-killpriv");
IFERT("handle-killpriv-v2");
IFERT("mount");
IFERT("nullrw");
IFERT("pid");
@ -103,6 +105,8 @@ Config::Config()
fsname(),
func(),
fuse_msg_size(FUSE_MAX_MAX_PAGES),
handle_killpriv(true),
handle_killpriv_v2(true),
ignorepponrename(false),
inodecalc("hybrid-hash"),
lazy_umount_mountpoint(false),
@ -114,6 +118,7 @@ Config::Config()
nfsopenhack(NFSOpenHack::ENUM::OFF),
nullrw(false),
parallel_direct_writes(false),
passthrough(false),
posix_acl(false),
readahead(0),
readdir("seq"),
@ -180,6 +185,8 @@ Config::Config()
_map["func.unlink"] = &func.unlink;
_map["func.utimens"] = &func.utimens;
_map["fuse_msg_size"] = &fuse_msg_size;
_map["handle-killpriv"] = &handle_killpriv;
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
_map["ignorepponrename"] = &ignorepponrename;
_map["inodecalc"] = &inodecalc;
_map["kernel_cache"] = &kernel_cache;
@ -194,6 +201,7 @@ Config::Config()
_map["nullrw"] = &nullrw;
_map["pid"] = &pid;
_map["parallel-direct-writes"] = &parallel_direct_writes;
_map["passthrough"] = &passthrough;
_map["pin-threads"] = &fuse_pin_threads;
_map["posix_acl"] = &posix_acl;
_map["readahead"] = &readahead;

3
src/config.hpp

@ -125,6 +125,8 @@ public:
ConfigSTR fsname;
Funcs func;
ConfigUINT64 fuse_msg_size;
ConfigBOOL handle_killpriv;
ConfigBOOL handle_killpriv_v2;
ConfigBOOL ignorepponrename;
InodeCalc inodecalc;
ConfigBOOL kernel_cache;
@ -137,6 +139,7 @@ public:
NFSOpenHack nfsopenhack;
ConfigBOOL nullrw;
ConfigBOOL parallel_direct_writes;
ConfigBOOL passthrough;
ConfigGetPid pid;
ConfigBOOL posix_acl;
ConfigUINT64 readahead;

2
src/fileinfo.hpp

@ -31,12 +31,14 @@ public:
bool const direct_io_)
: FH(fusepath_),
fd(fd_),
backing_id(0),
direct_io(direct_io_)
{
}
public:
int fd;
int backing_id;
uint32_t direct_io:1;
std::mutex mutex;
};

27
src/fuse_create.cpp

@ -170,6 +170,28 @@ namespace l
return 0;
}
static
int
passthrough(const fuse_context *fc_,
fuse_file_info_t *ffi_)
{
int backing_id;
FileInfo *fi;
fi = reinterpret_cast<FileInfo*>(ffi_->fh);
backing_id = fuse_passthrough_open(fc_,fi->fd);
if(backing_id <= 0)
return 0;
ffi_->passthrough = true;
ffi_->keep_cache = false;
fi->backing_id = backing_id;
ffi_->backing_id = backing_id;
return 0;
}
static
int
create(const Policy::Search &searchFunc_,
@ -247,6 +269,9 @@ namespace FUSE
fc->umask);
}
return rv;
if((rv != 0) || (cfg->passthrough == false))
return rv;
return l::passthrough(fc,ffi_);
}
}

3
src/fuse_init.cpp

@ -147,8 +147,11 @@ namespace FUSE
l::want_if_capable(conn_,FUSE_CAP_DIRECT_IO_ALLOW_MMAP,&cfg->direct_io_allow_mmap);
l::want_if_capable(conn_,FUSE_CAP_DONT_MASK);
l::want_if_capable(conn_,FUSE_CAP_EXPORT_SUPPORT,&cfg->export_support);
l::want_if_capable(conn_,FUSE_CAP_HANDLE_KILLPRIV,&cfg->handle_killpriv);
l::want_if_capable(conn_,FUSE_CAP_HANDLE_KILLPRIV_V2,&cfg->handle_killpriv_v2);
l::want_if_capable(conn_,FUSE_CAP_IOCTL_DIR);
l::want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS);
l::want_if_capable(conn_,FUSE_CAP_PASSTHROUGH,&cfg->passthrough);
l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&cfg->posix_acl);
l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&cfg->readdirplus);
l::want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&cfg->writeback_cache);

27
src/fuse_open.cpp

@ -218,6 +218,28 @@ namespace l
return 0;
}
static
int
passthrough(const fuse_context *fc_,
fuse_file_info_t *ffi_)
{
int backing_id;
FileInfo *fi;
fi = reinterpret_cast<FileInfo*>(ffi_->fh);
backing_id = fuse_passthrough_open(fc_,fi->fd);
if(backing_id <= 0)
return 0;
ffi_->passthrough = true;
ffi_->keep_cache = false;
fi->backing_id = backing_id;
ffi_->backing_id = backing_id;
return 0;
}
static
int
open(const Policy::Search &searchFunc_,
@ -264,6 +286,9 @@ namespace FUSE
cfg->link_cow,
cfg->nfsopenhack);
return rv;
if((rv != 0) || (cfg->passthrough == false))
return rv;
return l::passthrough(fc,ffi_);
}
}

9
src/fuse_release.cpp

@ -40,6 +40,15 @@ namespace l
fs::fadvise_dontneed(fi_->fd);
}
if(fi_->backing_id)
{
const fuse_context *fc;
fc = fuse_get_context();
fuse_passthrough_close(fc,fi_->backing_id);
}
fs::close(fi_->fd);
delete fi_;

Loading…
Cancel
Save