summaryrefslogtreecommitdiff
path: root/fs/fuse/dev.c
diff options
context:
space:
mode:
authorJoanne Koong <joannelkoong@gmail.com>2026-04-08 10:25:10 -0700
committerMiklos Szeredi <mszeredi@redhat.com>2026-06-15 14:06:15 +0200
commit31da059891bd3be9c6e59280b8e1777ead90db34 (patch)
treecc6d76fdb5779b0c326626bfa044462542585c0c /fs/fuse/dev.c
parent9fa4f7a53406430ee9982f2f636a15b338185122 (diff)
downloadlinux-next-31da059891bd3be9c6e59280b8e1777ead90db34.tar.gz
linux-next-31da059891bd3be9c6e59280b8e1777ead90db34.zip
fuse: fix io-uring background queue dispatch on request completion
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> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r--fs/fuse/dev.c41
1 files changed, 24 insertions, 17 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 12b57a8c6024..916c214798ca 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -448,6 +448,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
@@ -480,23 +503,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 {