[Devel] [PATCH RHEL10 COMMIT] fs/fuse: force offload of final fput in aio completion to workqueue

Konstantin Khorenko khorenko at virtuozzo.com
Mon Jun 29 17:31:01 MSK 2026


The commit is pushed to "branch-rh10-6.12.0-211.16.1.12.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-211.16.1.12.5.vz10
------>
commit 4dc73eb6adc3d37cfde6a83345900a837679ba8c
Author: Alexey Kuznetsov <kuznet at virtuozzo.com>
Date:   Tue Jun 23 00:22:26 2026 +0800

    fs/fuse: force offload of final fput in aio completion to workqueue
    
    The patch is known as "fuse: queue work for aio_complete (v3)",
    here is its simpler form.
    
    Aio completion is executed in context of userspace worker thread.
    Final fput might make lots of additional work like updating mtime,
    releasing file, which could be routed to the same thread.
    The problem is not specific to vstorage, all decent fuse fses
    suffer of this. The code as is works only with low performance
    fses based on libfuse. Also, current version of vstorage
    is not supposed suffer if this problem because aio completions
    are guaranteed to be done in threads different of threads
    working on metadata. Yet, it can be configured, deliberately
    or by mistake to route everything to one thread.
    
    Use workqueue supplied by vfs, all kernels in use (rh7,9,10)
    offload final fput to workqueue when fput() is called from
    interrupt or kthread context. So, we grab reference to file
    before ki_complete() and release it afterwards surrounded
    with local_bh_disable/local_bh_enable pair. It looks like
    a good pragmatic solution with minimal impact on performance
    and maintanance cost.
    
    https://virtuozzo.atlassian.net/browse/VSTOR-135017
    Reviewed-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
    Signed-off-by: Alexey Kuznetsov <kuznet at virtuozzo.com>
    Feature: vStorage
---
 fs/fuse/file.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 40cded85037f0..d11aaac99f03c 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -971,6 +971,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
 
 	if (!left && !io->blocking) {
 		ssize_t res = fuse_get_res_by_io(io);
+		struct file *file = io->iocb->ki_filp;
 
 		if (res >= 0) {
 			struct inode *inode = file_inode(io->iocb->ki_filp);
@@ -989,7 +990,24 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
 			       io->iocb->ki_flags, io->iocb->ki_pos);
 		}
 
+		/*
+		 * We have to bump f_count here to avoid deadlock for
+		 * single-threaded fuse daemon: if the process that generated
+		 * AIO is already close(2) the file, fput() called from
+		 * aio_complete will be the last fput(); hence, it will send
+		 * flush_mtime (or release) request to userspace who is busy
+		 * now writing ACK for given AIO to in-kernel fuse
+		 */
+		get_file(file);
 		io->iocb->ki_complete(io->iocb, res);
+
+		/*
+		 * local_bh_disable() forces fput() to offload final
+		 * release to workqueue
+		 */
+		local_bh_disable();
+		fput(file);
+		local_bh_enable();
 	}
 
 	kref_put(&io->refcnt, fuse_io_release);


More information about the Devel mailing list