]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse: fix io-uring background queue dispatch on request completion
authorJoanne Koong <joannelkoong@gmail.com>
Wed, 8 Apr 2026 17:25:10 +0000 (10:25 -0700)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 15 Jun 2026 12:06:15 +0000 (14:06 +0200)
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>
fs/fuse/dev.c
fs/fuse/dev_uring.c
fs/fuse/fuse_dev_i.h

index 12b57a8c60244131deba15e85c9bf8279375f8be..916c214798ca31eba3a05c1cb6a3bfc015d162ce 100644 (file)
@@ -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 {
index 7e5fe462b935fd0d3b7bde3a9d0c7234fa0a0a03..8fe788b8ecbaa862fcb5f0e9839bf38bd2af8520 100644 (file)
@@ -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);
        }
index 910f883cd090fdd306fd3e8d59e4f4e8bd7feef5..1853940fe80a5550bd0c630ee0c4de2f65f4b484 100644 (file)
@@ -77,6 +77,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);