diff options
author | Amir Goldstein <amir73il@gmail.com> | 2024-05-13 16:30:25 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-13 15:30:25 +0200 |
commit | eca63dab456a10c3491c367711ab18cbcb34816e (patch) | |
tree | ef644bbcdb271db33525166e75c6b21b0bb04414 /lib/fuse_lowlevel.c | |
parent | 58f85bfa9b7dca9a216cd0bb4e38e9cdf4b661da (diff) | |
download | libfuse-eca63dab456a10c3491c367711ab18cbcb34816e.tar.gz |
Enable passthrough mode for read/write operations (#919)
Add support for filesystem passthrough read/write of files.
When the FUSE_PASSTHROUGH capability is enabled, the FUSE server may
decide, while handling the "open" or "create" requests, if the given
file can be accessed by that process in "passthrough" mode, meaning that
all the further read and write operations would be forwarded by the
kernel directly to the backing file rather than to the FUSE server.
All requests other than read or write are still handled by the server.
This allows for an improved performance on reads and writes, especially
in the case of reads at random offsets, for which no (readahead)
caching mechanism would help, reducing the performance gap between FUSE
and native filesystem access.
Extend also the passthrough_hp example with the new passthrough feature.
This example opens a kernel backing file per FUSE inode on the first
FUSE file open of that inode and closes the backing file on the release
of the last FUSE file on that inode.
All opens of the same inode passthrough to the same backing file.
A combination of fi->direct_io and fi->passthrough is allowed.
It means that read/write operations go directly to the server, but mmap
is done on the backing file.
This allows to open some fds of the inode in passthrough mode and some
fd of the same inode in direct_io/passthrough_mmap mode.
Signed-off-by: Alessio Balsini <balsini@android.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Diffstat (limited to 'lib/fuse_lowlevel.c')
-rw-r--r-- | lib/fuse_lowlevel.c | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index c08c99c..1f3a5fa 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -27,6 +27,7 @@ #include <errno.h> #include <assert.h> #include <sys/file.h> +#include <sys/ioctl.h> #ifndef F_LINUX_SPECIFIC_BASE #define F_LINUX_SPECIFIC_BASE 1024 @@ -400,6 +401,10 @@ static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f) { arg->fh = f->fh; + if (f->backing_id > 0) { + arg->backing_id = f->backing_id; + arg->open_flags |= FOPEN_PASSTHROUGH; + } if (f->direct_io) arg->open_flags |= FOPEN_DIRECT_IO; if (f->keep_cache) @@ -466,6 +471,31 @@ int fuse_reply_readlink(fuse_req_t req, const char *linkname) return send_reply_ok(req, linkname, strlen(linkname)); } +int fuse_passthrough_open(fuse_req_t req, int fd) +{ + struct fuse_backing_map map = { .fd = fd }; + int ret; + + ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map); + if (ret <= 0) { + fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno)); + return 0; + } + + return ret; +} + +int fuse_passthrough_close(fuse_req_t req, int backing_id) +{ + int ret; + + ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id); + if (ret < 0) + fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno)); + + return ret; +} + int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) { struct fuse_open_out arg; @@ -2027,6 +2057,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) se->conn.capable |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP; if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY)) se->conn.capable |= FUSE_CAP_EXPIRE_ONLY; + if (inargflags & FUSE_PASSTHROUGH) + se->conn.capable |= FUSE_CAP_PASSTHROUGH; } else { se->conn.max_readahead = 0; } @@ -2161,6 +2193,14 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) outargflags |= FUSE_SETXATTR_EXT; if (se->conn.want & FUSE_CAP_DIRECT_IO_ALLOW_MMAP) outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP; + if (se->conn.want & FUSE_CAP_PASSTHROUGH) { + outargflags |= FUSE_PASSTHROUGH; + /* + * outarg.max_stack_depth includes the fuse stack layer, + * so it is one more than max_backing_stack_depth. + */ + outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1; + } if (inargflags & FUSE_INIT_EXT) { outargflags |= FUSE_INIT_EXT; @@ -2199,6 +2239,9 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) outarg.congestion_threshold); fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); + if (se->conn.want & FUSE_CAP_PASSTHROUGH) + fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n", + outarg.max_stack_depth); } if (arg->minor < 5) outargsize = FUSE_COMPAT_INIT_OUT_SIZE; |