From: Joanne Koong <joannelkoong@gmail.com>
To: Miklos Szeredi <mszeredi@redhat.com>
Cc: linux-fsdevel@vger.kernel.org,
"Darrick J. Wong" <djwong@kernel.org>,
John Groves <John@groves.net>,
Bernd Schubert <bernd@bsbernd.com>
Subject: Re: [PATCH v2] fuse: allow synchronous FUSE_INIT
Date: Wed, 27 Aug 2025 15:56:49 -0700 [thread overview]
Message-ID: <CAJnrk1b8FZC82oeWuynWk5oqiRe+04frUv-4w9=jg319KvUz0A@mail.gmail.com> (raw)
In-Reply-To: <20250827110004.584582-1-mszeredi@redhat.com>
On Wed, Aug 27, 2025 at 4:00 AM Miklos Szeredi <mszeredi@redhat.com> wrote:
>
> FUSE_INIT has always been asynchronous with mount. That means that the
> server processed this request after the mount syscall returned.
>
> This means that FUSE_INIT can't supply the root inode's ID, hence it
> currently has a hardcoded value. There are other limitations such as not
> being able to perform getxattr during mount, which is needed by selinux.
>
> To remove these limitations allow server to process FUSE_INIT while
> initializing the in-core super block for the fuse filesystem. This can
> only be done if the server is prepared to handle this, so add
> FUSE_DEV_IOC_SYNC_INIT ioctl, which
>
> a) lets the server know whether this feature is supported, returning
> ENOTTY othewrwise.
>
> b) lets the kernel know to perform a synchronous initialization
>
> The implementation is slightly tricky, since fuse_dev/fuse_conn are set up
> only during super block creation. This is solved by setting the private
> data of the fuse device file to a special value ((struct fuse_dev *) 1) and
> waiting for this to be turned into a proper fuse_dev before commecing with
> operations on the device file.
>
> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
> ---
> v2:
>
> - make fuse_send_init() perform sync/async sequence based on fc->sync_init
> (Joanne)
>
> fs/fuse/cuse.c | 3 +-
> fs/fuse/dev.c | 74 +++++++++++++++++++++++++++++----------
> fs/fuse/dev_uring.c | 4 +--
> fs/fuse/fuse_dev_i.h | 13 +++++--
> fs/fuse/fuse_i.h | 5 ++-
> fs/fuse/inode.c | 50 ++++++++++++++++++++------
> include/uapi/linux/fuse.h | 1 +
> 7 files changed, 115 insertions(+), 35 deletions(-)
>
> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
> index 8ac074414897..948f45c6e0ef 100644
> --- a/fs/fuse/dev.c
> +++ b/fs/fuse/dev.c
> @@ -1530,14 +1530,34 @@ static int fuse_dev_open(struct inode *inode, struct file *file)
> return 0;
> }
>
> +struct fuse_dev *fuse_get_dev(struct file *file)
> +{
> + struct fuse_dev *fud = __fuse_get_dev(file);
> + int err;
> +
> + if (likely(fud))
> + return fud;
> +
> + err = wait_event_interruptible(fuse_dev_waitq,
> + READ_ONCE(file->private_data) != FUSE_DEV_SYNC_INIT);
I wonder if we should make the semantics the same for synchronous and
non-synchronous inits here, i.e. doing a wait for
"(READ_ONCE(file->private_data) != FUSE_DEV_SYNC_INIT) &&
READ_ONCE(file->private_data) != NULL", so that from the libfuse point
of view, the flow can be unified between the two, eg
i) send sync_init ioctl call if doing a synchronous init
ii) kick off thread to read requests
iii) do mount call
otherwise for async inits, the mount call needs to happen first.
> + if (err)
> + return ERR_PTR(err);
> +
> + fud = __fuse_get_dev(file);
> + if (!fud)
> + return ERR_PTR(-EPERM);
> +
> + return fud;
> +}
> +
> static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to)
> {
> struct fuse_copy_state cs;
> struct file *file = iocb->ki_filp;
> struct fuse_dev *fud = fuse_get_dev(file);
>
> - if (!fud)
> - return -EPERM;
> + if (IS_ERR(fud))
> + return PTR_ERR(fud);
>
> if (!user_backed_iter(to))
> return -EINVAL;
> @@ -1557,8 +1577,8 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
> struct fuse_copy_state cs;
> struct fuse_dev *fud = fuse_get_dev(in);
>
> - if (!fud)
> - return -EPERM;
> + if (IS_ERR(fud))
> + return PTR_ERR(fud);
>
> bufs = kvmalloc_array(pipe->max_usage, sizeof(struct pipe_buffer),
> GFP_KERNEL);
> @@ -2233,8 +2253,8 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from)
> struct fuse_copy_state cs;
> struct fuse_dev *fud = fuse_get_dev(iocb->ki_filp);
Does this (and below in fuse_dev_splice_write()) need to be
fuse_get_dev()? afaict, fuse_dev_write() only starts getting used
after fud has already been initialized. i see why it's needed for
fuse_dev_read() since otherwise the server doesn't know when it can
start calling fuse_dev_read(), but for fuse_dev_write(), it seems like
that only gets used after fud is already initialized.
>
> - if (!fud)
> - return -EPERM;
> + if (IS_ERR(fud))
> + return PTR_ERR(fud);
>
> if (!user_backed_iter(from))
> return -EINVAL;
> @@ -2258,8 +2278,8 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
> ssize_t ret;
>
> fud = fuse_get_dev(out);
> - if (!fud)
> - return -EPERM;
> + if (IS_ERR(fud))
> + return PTR_ERR(fud);
>
> pipe_lock(pipe);
>
> @@ -2581,8 +2601,8 @@ static long fuse_dev_ioctl_backing_open(struct file *file,
> struct fuse_dev *fud = fuse_get_dev(file);
Should this be __fuse_get_dev()?
> struct fuse_backing_map map;
>
> - if (!fud)
> - return -EPERM;
> + if (IS_ERR(fud))
> + return PTR_ERR(fud);
>
> if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
> return -EOPNOTSUPP;
> @@ -2598,8 +2618,8 @@ static long fuse_dev_ioctl_backing_close(struct file *file, __u32 __user *argp)
> struct fuse_dev *fud = fuse_get_dev(file);
Same question here.
> int backing_id;
>
> - if (!fud)
> - return -EPERM;
> + if (IS_ERR(fud))
> + return PTR_ERR(fud);
>
> if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
> return -EOPNOTSUPP;
> @@ -2610,6 +2630,19 @@ static long fuse_dev_ioctl_backing_close(struct file *file, __u32 __user *argp)
> return fuse_backing_close(fud->fc, backing_id);
> }
>
> +static long fuse_dev_ioctl_sync_init(struct file *file)
> +{
> + int err = -EINVAL;
> +
> + mutex_lock(&fuse_mutex);
> + if (!__fuse_get_dev(file)) {
> + WRITE_ONCE(file->private_data, FUSE_DEV_SYNC_INIT);
Does this still need a WRITE_ONCE if it's accessed within the scope of
the mutex? My understanding (maybe wrong) is that a mutex implicitly
serves as also a memory barrier. If not, then we probably also need a
WRITE_ONCE() around the *ctx->fudptr assignment in
fuse_fill_super_common()?
Thanks,
Joanne
> + err = 0;
> + }
> + mutex_unlock(&fuse_mutex);
> + return err;
> +}
> +
> @@ -1876,8 +1901,10 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
>
> list_add_tail(&fc->entry, &fuse_conn_list);
> sb->s_root = root_dentry;
> - if (ctx->fudptr)
> + if (ctx->fudptr) {
> *ctx->fudptr = fud;
> + wake_up_all(&fuse_dev_waitq);
> + }
> mutex_unlock(&fuse_mutex);
> return 0;
>
next prev parent reply other threads:[~2025-08-27 22:57 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-27 10:59 [PATCH v2] fuse: allow synchronous FUSE_INIT Miklos Szeredi
2025-08-27 19:02 ` Darrick J. Wong
2025-08-27 19:51 ` Miklos Szeredi
2025-08-27 20:26 ` Darrick J. Wong
2025-08-27 22:56 ` Joanne Koong [this message]
2025-08-28 0:36 ` Darrick J. Wong
2025-08-28 13:09 ` Miklos Szeredi
2025-08-28 16:06 ` Joanne Koong
2025-08-29 15:43 ` Darrick J. Wong
2025-08-29 16:11 ` Bernd Schubert
2025-08-29 16:15 ` Miklos Szeredi
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='CAJnrk1b8FZC82oeWuynWk5oqiRe+04frUv-4w9=jg319KvUz0A@mail.gmail.com' \
--to=joannelkoong@gmail.com \
--cc=John@groves.net \
--cc=bernd@bsbernd.com \
--cc=djwong@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=mszeredi@redhat.com \
/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).