From: Amir Goldstein <amir73il@gmail.com>
To: Miklos Szeredi <miklos@szeredi.hu>
Cc: Bernd Schubert <bernd.schubert@fastmail.fm>,
linux-fsdevel@vger.kernel.org
Subject: [PATCH v15 4/9] fuse: prepare for opening file in passthrough mode
Date: Tue, 6 Feb 2024 16:24:48 +0200 [thread overview]
Message-ID: <20240206142453.1906268-5-amir73il@gmail.com> (raw)
In-Reply-To: <20240206142453.1906268-1-amir73il@gmail.com>
In preparation for opening file in passthrough mode, store the
fuse_open_out argument in ff->args to be passed into fuse_file_io_open()
with the optional backing_id member.
This will be used for setting up passthrough to backing file on open
reply with FOPEN_PASSTHROUGH flag and a valid backing_id.
Opening a file in passthrough mode may fail for several reasons, such as
missing capability, conflicting open flags or inode in caching mode.
Return EIO from fuse_file_io_open() in those cases.
The combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO is allowed -
it mean that read/write operations will go directly to the server,
but mmap will be done to the backing file.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/fuse/dir.c | 12 +++++++-----
fs/fuse/file.c | 34 +++++++++++++++-------------------
fs/fuse/fuse_i.h | 19 ++++++++++++++++---
fs/fuse/iomode.c | 48 +++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 79 insertions(+), 34 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index ea635c17572a..95330c2ca3d8 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -615,7 +615,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
FUSE_ARGS(args);
struct fuse_forget_link *forget;
struct fuse_create_in inarg;
- struct fuse_open_out outopen;
+ struct fuse_open_out *outopenp;
struct fuse_entry_out outentry;
struct fuse_inode *fi;
struct fuse_file *ff;
@@ -659,8 +659,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
args.out_numargs = 2;
args.out_args[0].size = sizeof(outentry);
args.out_args[0].value = &outentry;
- args.out_args[1].size = sizeof(outopen);
- args.out_args[1].value = &outopen;
+ /* Store outarg for fuse_finish_open() */
+ outopenp = &ff->args->open_outarg;
+ args.out_args[1].size = sizeof(*outopenp);
+ args.out_args[1].value = outopenp;
err = get_create_ext(&args, dir, entry, mode);
if (err)
@@ -676,9 +678,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
fuse_invalid_attr(&outentry.attr))
goto out_free_ff;
- ff->fh = outopen.fh;
+ ff->fh = outopenp->fh;
ff->nodeid = outentry.nodeid;
- ff->open_flags = outopen.open_flags;
+ ff->open_flags = outopenp->open_flags;
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
&outentry.attr, ATTR_TIMEOUT(&outentry), 0);
if (!inode) {
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index eb226457c4bd..04be04b6b2af 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -50,12 +50,6 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
return fuse_simple_request(fm, &args);
}
-struct fuse_release_args {
- struct fuse_args args;
- struct fuse_release_in inarg;
- struct inode *inode;
-};
-
struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
{
struct fuse_file *ff;
@@ -66,9 +60,8 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
ff->fm = fm;
if (release) {
- ff->release_args = kzalloc(sizeof(*ff->release_args),
- GFP_KERNEL_ACCOUNT);
- if (!ff->release_args) {
+ ff->args = kzalloc(sizeof(*ff->args), GFP_KERNEL_ACCOUNT);
+ if (!ff->args) {
kfree(ff);
return NULL;
}
@@ -87,7 +80,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
void fuse_file_free(struct fuse_file *ff)
{
- kfree(ff->release_args);
+ kfree(ff->args);
mutex_destroy(&ff->readdir.lock);
kfree(ff);
}
@@ -110,7 +103,7 @@ static void fuse_release_end(struct fuse_mount *fm, struct fuse_args *args,
static void fuse_file_put(struct fuse_file *ff, bool sync)
{
if (refcount_dec_and_test(&ff->count)) {
- struct fuse_release_args *ra = ff->release_args;
+ struct fuse_release_args *ra = &ff->args->release_args;
struct fuse_args *args = (ra ? &ra->args : NULL);
if (ra && ra->inode)
@@ -147,20 +140,21 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
/* Default for no-open */
ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0);
if (!noopen) {
- struct fuse_open_out outarg;
+ /* Store outarg for fuse_finish_open() */
+ struct fuse_open_out *outargp = &ff->args->open_outarg;
int err;
- err = fuse_send_open(fm, nodeid, open_flags, opcode, &outarg);
+ err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp);
if (!err) {
- ff->fh = outarg.fh;
- ff->open_flags = outarg.open_flags;
+ ff->fh = outargp->fh;
+ ff->open_flags = outargp->open_flags;
} else if (err != -ENOSYS) {
fuse_file_free(ff);
return ERR_PTR(err);
} else {
/* No release needed */
- kfree(ff->release_args);
- ff->release_args = NULL;
+ kfree(ff->args);
+ ff->args = NULL;
if (isdir)
fc->no_opendir = 1;
else
@@ -299,7 +293,7 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,
unsigned int flags, int opcode, bool sync)
{
struct fuse_conn *fc = ff->fm->fc;
- struct fuse_release_args *ra = ff->release_args;
+ struct fuse_release_args *ra = &ff->args->release_args;
/* Inode is NULL on error path of fuse_create_open() */
if (likely(fi)) {
@@ -317,6 +311,8 @@ static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,
if (!ra)
return;
+ /* ff->args was used for open outarg */
+ memset(ff->args, 0, sizeof(*ff->args));
ra->inarg.fh = ff->fh;
ra->inarg.flags = flags;
ra->args.in_numargs = 1;
@@ -339,7 +335,7 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff,
unsigned int open_flags, fl_owner_t id, bool isdir)
{
struct fuse_inode *fi = get_fuse_inode(inode);
- struct fuse_release_args *ra = ff->release_args;
+ struct fuse_release_args *ra = &ff->args->release_args;
int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
fuse_prepare_release(fi, ff, open_flags, opcode, false);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index fb9ef02cbf45..eea8f1ffc766 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -213,15 +213,15 @@ enum {
struct fuse_conn;
struct fuse_mount;
-struct fuse_release_args;
+union fuse_file_args;
/** FUSE specific file data */
struct fuse_file {
/** Fuse connection for this file */
struct fuse_mount *fm;
- /* Argument space reserved for release */
- struct fuse_release_args *release_args;
+ /* Argument space reserved for open/release */
+ union fuse_file_args *args;
/** Kernel file handle guaranteed to be unique */
u64 kh;
@@ -320,6 +320,19 @@ struct fuse_args_pages {
unsigned int num_pages;
};
+struct fuse_release_args {
+ struct fuse_args args;
+ struct fuse_release_in inarg;
+ struct inode *inode;
+};
+
+union fuse_file_args {
+ /* Used during open() */
+ struct fuse_open_out open_outarg;
+ /* Used during release() */
+ struct fuse_release_args release_args;
+};
+
#define FUSE_ARGS(args) struct fuse_args args = {}
/** The request IO state (for asynchronous processing) */
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index acd0833ae873..48105f3c00f6 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -138,9 +138,40 @@ void fuse_file_uncached_io_end(struct inode *inode)
wake_up(&fi->direct_io_waitq);
}
+/*
+ * Open flags that are allowed in combination with FOPEN_PASSTHROUGH.
+ * A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write
+ * operations go directly to the server, but mmap is done on the backing file.
+ * FOPEN_PASSTHROUGH mode should not co-exist with any users of the fuse inode
+ * page cache, so FOPEN_KEEP_CACHE is a strange and undesired combination.
+ */
+#define FOPEN_PASSTHROUGH_MASK \
+ (FOPEN_PASSTHROUGH | FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES | \
+ FOPEN_NOFLUSH)
+
+static int fuse_file_passthrough_open(struct file *file, struct inode *inode)
+{
+ struct fuse_file *ff = file->private_data;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ int err;
+
+ /* Check allowed conditions for file open in passthrough mode */
+ if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough ||
+ (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK))
+ return -EINVAL;
+
+ /* TODO: implement backing file open */
+ return -EOPNOTSUPP;
+
+ /* First passthrough file open denies caching inode io mode */
+ err = fuse_file_uncached_io_start(inode);
+
+ return err;
+}
+
/* Open flags to determine regular file io mode */
#define FOPEN_IO_MODE_MASK \
- (FOPEN_DIRECT_IO | FOPEN_CACHE_IO)
+ (FOPEN_DIRECT_IO | FOPEN_CACHE_IO | FOPEN_PASSTHROUGH)
/* Request access to submit new io to inode via open file */
int fuse_file_io_open(struct file *file, struct inode *inode)
@@ -162,7 +193,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
* implement open.
*/
err = -EINVAL;
- if (FUSE_IS_DAX(inode) || !ff->release_args) {
+ if (FUSE_IS_DAX(inode) || !ff->args) {
if (iomode_flags)
goto fail;
return 0;
@@ -170,7 +201,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
/*
* FOPEN_CACHE_IO is an internal flag that is set on file not open in
- * direct io mode and it cannot be set explicitly by the server.
+ * direct io or passthrough mode and it cannot be set by the server.
* This includes a file open with O_DIRECT, but server did not specify
* FOPEN_DIRECT_IO. In this case, a later fcntl() could remove O_DIRECT,
* so we put the inode in caching mode to prevent parallel dio.
@@ -178,7 +209,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
*/
if (ff->open_flags & FOPEN_CACHE_IO) {
goto fail;
- } else if (!(ff->open_flags & FOPEN_DIRECT_IO)) {
+ } else if (!(ff->open_flags & FOPEN_IO_MODE_MASK)) {
ff->open_flags |= FOPEN_CACHE_IO;
ff->open_flags &= ~FOPEN_PARALLEL_DIRECT_WRITES;
}
@@ -189,6 +220,8 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
err = 0;
if (ff->open_flags & FOPEN_CACHE_IO)
err = fuse_file_cached_io_start(inode);
+ else if (ff->open_flags & FOPEN_PASSTHROUGH)
+ err = fuse_file_passthrough_open(file, inode);
if (err)
goto fail;
@@ -206,17 +239,18 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
return -EIO;
}
-/* Request access to submit new io to inode via mmap */
+/* Request access to submit cached io to inode via mmap */
int fuse_file_io_mmap(struct fuse_file *ff, struct inode *inode)
{
struct fuse_inode *fi = get_fuse_inode(inode);
int err = 0;
/* There are no io modes if server does not implement open */
- if (!ff->release_args)
+ if (!ff->args)
return 0;
- if (WARN_ON(!ff->io_opened))
+ if (WARN_ON(ff->open_flags & FOPEN_PASSTHROUGH) ||
+ WARN_ON(!ff->io_opened))
return -ENODEV;
spin_lock(&fi->lock);
--
2.34.1
next prev parent reply other threads:[~2024-02-06 14:25 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-06 14:24 [PATCH v15 0/9] FUSE passthrough for file io Amir Goldstein
2024-02-06 14:24 ` [PATCH v15 1/9] fuse: factor out helper for FUSE_DEV_IOC_CLONE Amir Goldstein
2024-02-06 14:24 ` [PATCH v15 2/9] fuse: introduce FUSE_PASSTHROUGH capability Amir Goldstein
2024-02-06 14:24 ` [PATCH v15 3/9] fuse: implement ioctls to manage backing files Amir Goldstein
2024-02-28 10:50 ` Jingbo Xu
2024-02-28 11:07 ` Amir Goldstein
2024-02-28 11:14 ` Miklos Szeredi
2024-02-28 11:28 ` Amir Goldstein
2024-02-28 14:32 ` Jens Axboe
2024-02-28 15:01 ` Miklos Szeredi
2024-02-28 15:05 ` Jens Axboe
2024-02-28 16:21 ` Amir Goldstein
2024-02-29 10:15 ` Christian Brauner
2024-02-29 10:17 ` Christian Brauner
2024-03-05 10:57 ` Miklos Szeredi
2024-02-28 13:22 ` Bernd Schubert
2024-02-06 14:24 ` Amir Goldstein [this message]
2024-02-06 14:24 ` [PATCH v15 5/9] fuse: implement open in passthrough mode Amir Goldstein
2024-02-06 14:24 ` [PATCH v15 6/9] fuse: implement read/write passthrough Amir Goldstein
2024-02-06 14:24 ` [PATCH v15 7/9] fuse: implement splice " Amir Goldstein
2024-02-06 14:24 ` [PATCH v15 8/9] fuse: implement passthrough for mmap Amir Goldstein
2024-02-06 14:24 ` [PATCH v15 9/9] fuse: auto-invalidate inode attributes in passthrough mode Amir Goldstein
2024-04-02 20:13 ` Sweet Tea Dorminy
2024-04-02 21:18 ` Bernd Schubert
2024-04-03 8:18 ` Amir Goldstein
2024-04-04 14:07 ` Sweet Tea Dorminy
2024-05-09 14:32 ` Amir Goldstein
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240206142453.1906268-5-amir73il@gmail.com \
--to=amir73il@gmail.com \
--cc=bernd.schubert@fastmail.fm \
--cc=linux-fsdevel@vger.kernel.org \
--cc=miklos@szeredi.hu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).