From: Bernd Schubert <bernd@bsbernd.com>
To: Joanne Koong <joannelkoong@gmail.com>,
Miklos Szeredi <miklos@szeredi.hu>
Cc: jlayton@kernel.org, libaokun@linux.alibaba.com, axboe@kernel.dk,
amir73il@gmail.com, fuse-devel@lists.linux.dev
Subject: Re: [PATCH v7 1/6] fuse: decouple fuse_ring creation from ent registration
Date: Wed, 19 Aug 2026 22:05:26 +0200 [thread overview]
Message-ID: <f0cc2fd8-bf0a-4722-b81c-d54fbafd00a7@bsbernd.com> (raw)
In-Reply-To: <CAJnrk1bocn3zSUe23E4F6oTNyosmF8kdVdFB-2hX2qbSqx-LDA@mail.gmail.com>
On 8/19/26 19:56, Joanne Koong wrote:
> On Wed, Aug 19, 2026 at 4:35 AM Miklos Szeredi <miklos@szeredi.hu> wrote:
>>
>> On Fri, 14 Aug 2026 at 21:00, Joanne Koong <joannelkoong@gmail.com> wrote:
>>>
>>> Currently, the connection's fuse_ring is created lazily on the first
>>> FUSE_IO_URING_CMD_REGISTER command. A server registers entries from one
>>> thread per queue (one per CPU) and those threads issue their first
>>> REGISTER command concurrently. They then race to create the single
>>> per-connection fuse_ring, which required open-coded handling in
>>> fuse_uring_create() to detect and protect against concurrent creations.
>>>
>>> Decouple fuse_ring creation from ent registration and move it to
>>> FUSE_INIT reply processing after a server has negotiated and set
>>> FUSE_OVER_IO_URING. The ring is published before the connection is
>>> marked initialized. fuse_uring_register() no longer creates the ring and
>>> it instead uses the ring set up at init time.
>>
>> I tested this with loraw (a "raw" loopback tester that doesn't use
>> libfuse) and it fails with
>>
>> root@kvm:~# ./loraw -u /mnt/fuse
>> loraw: loraw.c:1010: lo_start_uring: Assertion `!cqe->res' failed.
>>
>> cqe->res is -22 (EINVAL).
>>
>> Attaching the reproducer. To compile:
>>
>> cp $(KERNEL_TREE)/include/uapi/linux/fuse.h fuse_kernel.h
>> gcc loraw.c -oloraw -luring
>>
>
> Thanks for attaching the repro.
>
> This is happening because this patch uses the FUSE_OVER_IO_URING init
> reply as a signal that the ring should be created, but I missed that
> the FUSE_OVER_IO_URING reply is *optional*.
>
> Prior to this patch, there's two scenarios:
> a) server sets FUSE_OVER_IO_URING reply at init time - requests will
> automatically block until fuse-io-uring is completely set up
> b) server does not set FUSE_OVER_IO_URING but later sends uring
> register request - requests will continue along /dev/fuse path until
> fuse-io-uring is completely set up
>
> Libfuse sets FUSE_OVER_IO_URING in the reply, but the loraw.c server does not.
>
> I think the best way to fix this is to have the ring creation happen
> when the kernel receives the first io-uring command instead of at
> FUSE_INIT or at FUSE_IO_URING_CMD_REGISTER ent creation time, given
> that FUSE_IO_URING_ADD_QUEUE needs the ring to exist:
I don't think we should allow io-uring without FUSE_OVER_IO_URING and
I really thought that was disabled.
<... checking the code ...>
I'm on a ublk branch without your commits a applied, i.e. plain upstream 7.2 fuse
fuse_uring_cmd()
/* Once a connection has io-uring enabled on it, it can't be disabled */
if (!enable_uring && !fch->io_uring) {
pr_info_ratelimited("fuse-io-uring is disabled\n");
return -EOPNOTSUPP;
}
In process_init_reply()
if (flags & FUSE_OVER_IO_URING && fuse_uring_enabled())
fuse_chan_io_uring_enable(fc->chan);
And this is also absolutely needed to block requests in fuse_block_alloc(),
which is a requirement to avoid lock order issues between queue->lock and
fch->bg_lock (at least I believe that has not been solved yet).
I'm going to try Miklos' reproducer in a bit, but one way or the other
let's please not allow io-uring without FUSE_OVER_IO_URING reply.
Thanks,
Bernd
>
> Subject: [PATCH] fuse: create fuse_ring on the first io-uring command
>
> Commit 6330b1f61ed1 ("fuse: decouple fuse_ring creation from ent
> registration") moved fuse_ring creation to FUSE_INIT reply processing,
> gated on the server setting FUSE_OVER_IO_URING in its reply flags.
>
> However, that flag is optional. Libfuse sets it but servers not going
> through libfuse may not.
>
> Create the ring on the first io-uring command instead, independent of
> what the server negotiated. Ring creation stays decoupled from ent
> registration, which FUSE_IO_URING_CMD_ADD_QUEUE depends on since it
> needs the ring to exist before any entry is registered.
>
> Fixes: 6330b1f61ed1 ("fuse: decouple fuse_ring creation from ent registration")
> Reported-by: Miklos Szeredi <mszeredi@redhat.com>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> ---
> fs/fuse/dev.c | 2 +-
> fs/fuse/dev_uring.c | 18 +++++++++++-------
> fs/fuse/dev_uring_i.h | 5 -----
> 3 files changed, 12 insertions(+), 13 deletions(-)
>
> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
> index 4fec31fc0b84..a665d76292c9 100644
> --- a/fs/fuse/dev.c
> +++ b/fs/fuse/dev.c
> @@ -77,7 +77,7 @@ void fuse_chan_set_initialized(struct fuse_chan
> *fch, struct fuse_chan_param *pa
> fch->max_pages = param->max_pages;
>
> if (param->io_uring_enabled)
> - fuse_uring_conn_init(fch);
> + fch->io_uring = 1;
> }
>
> /* Pairs with smp_load_acquire() readers of fch->initialized */
> diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
> index e22a48c9a678..23c26099d159 100644
> --- a/fs/fuse/dev_uring.c
> +++ b/fs/fuse/dev_uring.c
> @@ -303,6 +303,7 @@ static struct fuse_ring *fuse_uring_create(struct
> fuse_chan *fch)
> {
> struct fuse_ring *ring;
> size_t nr_queues = num_possible_cpus();
> + struct fuse_ring *res = NULL;
> size_t max_payload_size;
>
> ring = kzalloc_obj(*ring, GFP_KERNEL_ACCOUNT);
> @@ -322,6 +323,12 @@ static struct fuse_ring *fuse_uring_create(struct
> fuse_chan *fch)
> spin_unlock(&fch->lock);
> goto out_err;
> }
> + if (fch->ring) {
> + /* race, another thread created the ring in the meantime */
> + spin_unlock(&fch->lock);
> + res = fch->ring;
> + goto out_err;
> + }
>
> init_waitqueue_head(&ring->stop_waitq);
>
> @@ -336,13 +343,7 @@ static struct fuse_ring *fuse_uring_create(struct
> fuse_chan *fch)
> out_err:
> kfree(ring->queues);
> kfree(ring);
> - return NULL;
> -}
> -
> -void fuse_uring_conn_init(struct fuse_chan *fch)
> -{
> - if (fuse_uring_create(fch))
> - fch->io_uring = 1;
> + return res;
> }
>
> static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring,
> @@ -1685,6 +1686,9 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd,
> unsigned int issue_flags)
> if (!smp_load_acquire(&fch->initialized))
> return -EAGAIN;
>
> + if (!smp_load_acquire(&fch->ring) && !fuse_uring_create(fch))
> + return -ENOMEM;
> +
> switch (cmd_op) {
> case FUSE_IO_URING_CMD_REGISTER:
> err = fuse_uring_register(cmd, issue_flags, fch);
> diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h
> index 263d0f8b9714..3233b07430d2 100644
> --- a/fs/fuse/dev_uring_i.h
> +++ b/fs/fuse/dev_uring_i.h
> @@ -184,7 +184,6 @@ struct fuse_ring {
> bool ready;
> };
>
> -void fuse_uring_conn_init(struct fuse_chan *fch);
> void fuse_uring_stop_queues(struct fuse_ring *ring);
> void fuse_uring_abort_end_requests(struct fuse_ring *ring);
> int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags);
> @@ -224,10 +223,6 @@ static inline bool fuse_uring_ready(struct fuse_chan *fch)
>
> #else /* CONFIG_FUSE_IO_URING */
>
> -static inline void fuse_uring_conn_init(struct fuse_chan *fch)
> -{
> -}
> -
> static inline void fuse_uring_abort(struct fuse_chan *fch)
> {
> }
> --
> 2.52.0
>
>
> If you'd prefer an inline replacement for the original commit instead
> of a fixup patch on top of the tree, please let me know and I'd be
> happy to send that over. Whatever would be easiest for you.
>
> Thanks,
> Joanne
next prev parent reply other threads:[~2026-08-19 20:05 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-14 18:59 [PATCH v7 0/6] fuse: add io-uring buffer pools and zero-copy Joanne Koong
2026-08-14 18:59 ` [PATCH v7 1/6] fuse: decouple fuse_ring creation from ent registration Joanne Koong
2026-08-19 11:34 ` Miklos Szeredi
2026-08-19 11:38 ` Bernd Schubert
2026-08-19 17:56 ` Joanne Koong
2026-08-19 20:05 ` Bernd Schubert [this message]
2026-08-19 20:29 ` Joanne Koong
2026-08-19 20:52 ` Bernd Schubert
2026-08-19 21:35 ` Bernd Schubert
2026-08-20 8:02 ` Baokun Li
2026-08-20 16:16 ` Joanne Koong
2026-08-20 17:20 ` Bernd Schubert
2026-08-20 17:46 ` Joanne Koong
2026-08-20 18:27 ` Bernd Schubert
2026-08-21 3:38 ` Baokun Li
2026-08-21 3:24 ` Baokun Li
2026-08-21 3:04 ` Baokun Li
2026-08-14 18:59 ` [PATCH v7 2/6] fuse: add FUSE_IO_URING_CMD_ADD_QUEUE Joanne Koong
2026-08-14 18:59 ` [PATCH v7 3/6] fuse: add io-uring buffer pools Joanne Koong
2026-08-14 18:59 ` [PATCH v7 4/6] fuse: support registered buffer pools in io-uring Joanne Koong
2026-08-17 10:15 ` Bernd Schubert
2026-08-14 18:59 ` [PATCH v7 5/6] fuse: add zero-copy over io-uring Joanne Koong
2026-08-17 13:05 ` Bernd Schubert
2026-08-14 18:59 ` [PATCH v7 6/6] docs: fuse: document io-uring buffer pool and zero-copy uapi Joanne Koong
2026-08-14 19:23 ` [PATCH v7 0/6] fuse: add io-uring buffer pools and zero-copy Joanne Koong
2026-08-17 15:29 ` Miklos Szeredi
2026-08-17 18:23 ` Jens Axboe
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=f0cc2fd8-bf0a-4722-b81c-d54fbafd00a7@bsbernd.com \
--to=bernd@bsbernd.com \
--cc=amir73il@gmail.com \
--cc=axboe@kernel.dk \
--cc=fuse-devel@lists.linux.dev \
--cc=jlayton@kernel.org \
--cc=joannelkoong@gmail.com \
--cc=libaokun@linux.alibaba.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.