* [PATCH v2] fuse: fix io-uring background queue dispatch on request completion
@ 2026-04-08 17:25 Joanne Koong
2026-04-16 14:43 ` Miklos Szeredi
0 siblings, 1 reply; 3+ messages in thread
From: Joanne Koong @ 2026-04-08 17:25 UTC (permalink / raw)
To: miklos; +Cc: bernd, hbirthelmer, linux-fsdevel, stable
When a background request completes via the io_uring path, the
background queue gets flushed to dispatch pending background requests,
but this is done before the connection-level background counters
(fc->num_background, fc->active_background) are properly accounted,
which may reduce effective queue depth to one.
The connection-level counters are decremented in fuse_request_end(), but
flush_bg_queue() flushes the /dev/fuse path queue (fc->bg_queue), not
the io_uring per-queue bg one, which means pending uring background
requests on the queue are never dispatched in this path.
Fix this by accounting the connection-level background counters first
before flushing the queue's background queue. Since
fuse_request_bg_finish() clears FR_BACKGROUND, fuse_request_end() will
skip the background cleanup branch entirely, which avoids any
double-decrements; it will call the wake_up(&req->waitq) branch but this
is effectively a no-op as background requests have no waiters on
req->waitq.
Reviewed-by: Bernd Schubert <bernd@bsbernd.com>
Fixes: 857b0263f30e ("fuse: Allow to queue bg requests through io-uring")
Cc: stable@vger.kernel.org
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
v1: https://lore.kernel.org/linux-fsdevel/20260401184915.747714-1-joannelkoong@gmail.com/
v1 -> v2:
* change commit message wording (Bernd)
---
fs/fuse/dev.c | 41 ++++++++++++++++++++++++-----------------
fs/fuse/dev_uring.c | 1 +
fs/fuse/fuse_dev_i.h | 1 +
3 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index b212565a78cf..35cdfc162ba5 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -447,6 +447,29 @@ static void flush_bg_queue(struct fuse_conn *fc)
}
}
+void fuse_request_bg_finish(struct fuse_conn *fc, struct fuse_req *req)
+{
+ lockdep_assert_held(&fc->bg_lock);
+
+ clear_bit(FR_BACKGROUND, &req->flags);
+ if (fc->num_background == fc->max_background) {
+ fc->blocked = 0;
+ wake_up(&fc->blocked_waitq);
+ } else if (!fc->blocked) {
+ /*
+ * Wake up next waiter, if any. It's okay to use
+ * waitqueue_active(), as we've already synced up
+ * fc->blocked with waiters with the wake_up() call
+ * above.
+ */
+ if (waitqueue_active(&fc->blocked_waitq))
+ wake_up(&fc->blocked_waitq);
+ }
+
+ fc->num_background--;
+ fc->active_background--;
+}
+
/*
* This function is called when a request is finished. Either a reply
* has arrived or it was aborted (and not yet sent) or some error
@@ -479,23 +502,7 @@ void fuse_request_end(struct fuse_req *req)
WARN_ON(test_bit(FR_SENT, &req->flags));
if (test_bit(FR_BACKGROUND, &req->flags)) {
spin_lock(&fc->bg_lock);
- clear_bit(FR_BACKGROUND, &req->flags);
- if (fc->num_background == fc->max_background) {
- fc->blocked = 0;
- wake_up(&fc->blocked_waitq);
- } else if (!fc->blocked) {
- /*
- * Wake up next waiter, if any. It's okay to use
- * waitqueue_active(), as we've already synced up
- * fc->blocked with waiters with the wake_up() call
- * above.
- */
- if (waitqueue_active(&fc->blocked_waitq))
- wake_up(&fc->blocked_waitq);
- }
-
- fc->num_background--;
- fc->active_background--;
+ fuse_request_bg_finish(fc, req);
flush_bg_queue(fc);
spin_unlock(&fc->bg_lock);
} else {
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 7b9822e8837b..ae916733f18a 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -90,6 +90,7 @@ static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req,
if (test_bit(FR_BACKGROUND, &req->flags)) {
queue->active_background--;
spin_lock(&fc->bg_lock);
+ fuse_request_bg_finish(fc, req);
fuse_uring_flush_bg(queue);
spin_unlock(&fc->bg_lock);
}
diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
index 134bf44aff0d..7da505af6d35 100644
--- a/fs/fuse/fuse_dev_i.h
+++ b/fs/fuse/fuse_dev_i.h
@@ -59,6 +59,7 @@ unsigned int fuse_req_hash(u64 unique);
struct fuse_req *fuse_request_find(struct fuse_pqueue *fpq, u64 unique);
void fuse_dev_end_requests(struct list_head *head);
+void fuse_request_bg_finish(struct fuse_conn *fc, struct fuse_req *req);
void fuse_copy_init(struct fuse_copy_state *cs, bool write,
struct iov_iter *iter);
--
2.52.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v2] fuse: fix io-uring background queue dispatch on request completion
2026-04-08 17:25 [PATCH v2] fuse: fix io-uring background queue dispatch on request completion Joanne Koong
@ 2026-04-16 14:43 ` Miklos Szeredi
2026-04-16 14:54 ` Bernd Schubert
0 siblings, 1 reply; 3+ messages in thread
From: Miklos Szeredi @ 2026-04-16 14:43 UTC (permalink / raw)
To: Joanne Koong; +Cc: bernd, hbirthelmer, linux-fsdevel, stable
On Wed, 8 Apr 2026 at 19:28, Joanne Koong <joannelkoong@gmail.com> wrote:
>
> When a background request completes via the io_uring path, the
> background queue gets flushed to dispatch pending background requests,
> but this is done before the connection-level background counters
> (fc->num_background, fc->active_background) are properly accounted,
> which may reduce effective queue depth to one.
>
> The connection-level counters are decremented in fuse_request_end(), but
> flush_bg_queue() flushes the /dev/fuse path queue (fc->bg_queue), not
> the io_uring per-queue bg one, which means pending uring background
> requests on the queue are never dispatched in this path.
>
> Fix this by accounting the connection-level background counters first
> before flushing the queue's background queue. Since
> fuse_request_bg_finish() clears FR_BACKGROUND, fuse_request_end() will
> skip the background cleanup branch entirely, which avoids any
> double-decrements; it will call the wake_up(&req->waitq) branch but this
> is effectively a no-op as background requests have no waiters on
> req->waitq.
Does this guarantee progress if there are still requests on
fc->bg_queue at the point when ring becomes ready?
Seems so, because there must be at least one background request on the
regular request queues if bg_queue is non-empty, and when that is
finished, a new one will be put on the pending queue, and so on until
bg_queue becomes empty.
Maybe add a comment about this subtlety?
Thanks,
Miklos
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2] fuse: fix io-uring background queue dispatch on request completion
2026-04-16 14:43 ` Miklos Szeredi
@ 2026-04-16 14:54 ` Bernd Schubert
0 siblings, 0 replies; 3+ messages in thread
From: Bernd Schubert @ 2026-04-16 14:54 UTC (permalink / raw)
To: Miklos Szeredi, Joanne Koong; +Cc: hbirthelmer, linux-fsdevel, stable
On 4/16/26 16:43, Miklos Szeredi wrote:
> On Wed, 8 Apr 2026 at 19:28, Joanne Koong <joannelkoong@gmail.com> wrote:
>>
>> When a background request completes via the io_uring path, the
>> background queue gets flushed to dispatch pending background requests,
>> but this is done before the connection-level background counters
>> (fc->num_background, fc->active_background) are properly accounted,
>> which may reduce effective queue depth to one.
>>
>> The connection-level counters are decremented in fuse_request_end(), but
>> flush_bg_queue() flushes the /dev/fuse path queue (fc->bg_queue), not
>> the io_uring per-queue bg one, which means pending uring background
>> requests on the queue are never dispatched in this path.
>>
>> Fix this by accounting the connection-level background counters first
>> before flushing the queue's background queue. Since
>> fuse_request_bg_finish() clears FR_BACKGROUND, fuse_request_end() will
>> skip the background cleanup branch entirely, which avoids any
>> double-decrements; it will call the wake_up(&req->waitq) branch but this
>> is effectively a no-op as background requests have no waiters on
>> req->waitq.
>
> Does this guarantee progress if there are still requests on
> fc->bg_queue at the point when ring becomes ready?
Request allocation is still blocked until the ring is set to ready -
there should be no background requests at all.
There were a couple of issues with dynamic switching from /dev/fuse to
fuse-io-uring, a really hard one was a lock order change. I think once
we have reduced queues merged, I'm to create an additional patch set
which allow allow distribution of requests between queues. The initial
series had that, but Joanne had concerns. Anyway, once we have
distribution between queues, we can avoid the global bg lock and global
bg limit and set that per queue. If I remember right, that would solve
the lock order issue. Although I forgot the exact details. Wish I would
have creates notes or a blog - too late now :(
>
> Seems so, because there must be at least one background request on the
> regular request queues if bg_queue is non-empty, and when that is
> finished, a new one will be put on the pending queue, and so on until
> bg_queue becomes empty.
Yeah, in order to avoid stalls, ring queues always allow one bg request,
ignoring the global limit.
>
> Maybe add a comment about this subtlety?
Where should that comment be?
Thanks,
Bernd
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-04-16 14:54 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-08 17:25 [PATCH v2] fuse: fix io-uring background queue dispatch on request completion Joanne Koong
2026-04-16 14:43 ` Miklos Szeredi
2026-04-16 14:54 ` Bernd Schubert
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.